mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-07 04:17:07 +00:00
Introduce Item use results - can be success, fail or none
closes #2693, closes #2705, closes #2734
This commit is contained in:
parent
d9bbe99b83
commit
dce08b4e88
@ -80,6 +80,7 @@ use pocketmine\item\Durable;
|
|||||||
use pocketmine\item\enchantment\EnchantmentInstance;
|
use pocketmine\item\enchantment\EnchantmentInstance;
|
||||||
use pocketmine\item\enchantment\MeleeWeaponEnchantment;
|
use pocketmine\item\enchantment\MeleeWeaponEnchantment;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\item\ItemUseResult;
|
||||||
use pocketmine\item\WritableBook;
|
use pocketmine\item\WritableBook;
|
||||||
use pocketmine\item\WrittenBook;
|
use pocketmine\item\WrittenBook;
|
||||||
use pocketmine\lang\TextContainer;
|
use pocketmine\lang\TextContainer;
|
||||||
@ -2061,11 +2062,14 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($item->onClickAir($this, $directionVector)){
|
$result = $item->onClickAir($this, $directionVector);
|
||||||
|
if($result === ItemUseResult::success()){
|
||||||
$this->resetItemCooldown($item);
|
$this->resetItemCooldown($item);
|
||||||
if($this->isSurvival()){
|
if($this->isSurvival()){
|
||||||
$this->inventory->setItemInHand($item);
|
$this->inventory->setItemInHand($item);
|
||||||
}
|
}
|
||||||
|
}elseif($result === ItemUseResult::fail()){
|
||||||
|
$this->inventory->sendHeldItem($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check if item has a release action - if it doesn't, this shouldn't be set
|
//TODO: check if item has a release action - if it doesn't, this shouldn't be set
|
||||||
@ -2120,11 +2124,16 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
|
|||||||
$this->inventory->sendContents($this);
|
$this->inventory->sendContents($this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if($item->onReleaseUsing($this)){
|
$result = $item->onReleaseUsing($this);
|
||||||
|
if($result === ItemUseResult::success()){
|
||||||
$this->resetItemCooldown($item);
|
$this->resetItemCooldown($item);
|
||||||
$this->inventory->setItemInHand($item);
|
$this->inventory->setItemInHand($item);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if($result === ItemUseResult::fail()){
|
||||||
|
$this->inventory->sendContents($this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -47,10 +47,9 @@ class Bow extends Tool{
|
|||||||
return 385;
|
return 385;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onReleaseUsing(Player $player) : bool{
|
public function onReleaseUsing(Player $player) : ItemUseResult{
|
||||||
if($player->isSurvival() and !$player->getInventory()->contains(ItemFactory::get(Item::ARROW, 0, 1))){
|
if($player->isSurvival() and !$player->getInventory()->contains(ItemFactory::get(Item::ARROW, 0, 1))){
|
||||||
$player->getInventory()->sendContents($player);
|
return ItemUseResult::fail();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$nbt = EntityFactory::createBaseNBT(
|
$nbt = EntityFactory::createBaseNBT(
|
||||||
@ -93,30 +92,32 @@ class Bow extends Tool{
|
|||||||
|
|
||||||
if($ev->isCancelled()){
|
if($ev->isCancelled()){
|
||||||
$entity->flagForDespawn();
|
$entity->flagForDespawn();
|
||||||
$player->getInventory()->sendContents($player);
|
return ItemUseResult::fail();
|
||||||
}else{
|
|
||||||
$entity->setMotion($entity->getMotion()->multiply($ev->getForce()));
|
|
||||||
if($player->isSurvival()){
|
|
||||||
if(!$infinity){ //TODO: tipped arrows are still consumed when Infinity is applied
|
|
||||||
$player->getInventory()->removeItem(ItemFactory::get(Item::ARROW, 0, 1));
|
|
||||||
}
|
|
||||||
$this->applyDamage(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($entity instanceof Projectile){
|
|
||||||
$projectileEv = new ProjectileLaunchEvent($entity);
|
|
||||||
$projectileEv->call();
|
|
||||||
if($projectileEv->isCancelled()){
|
|
||||||
$ev->getProjectile()->flagForDespawn();
|
|
||||||
}else{
|
|
||||||
$ev->getProjectile()->spawnToAll();
|
|
||||||
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
$entity->spawnToAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
$entity->setMotion($entity->getMotion()->multiply($ev->getForce()));
|
||||||
|
|
||||||
|
if($entity instanceof Projectile){
|
||||||
|
$projectileEv = new ProjectileLaunchEvent($entity);
|
||||||
|
$projectileEv->call();
|
||||||
|
if($projectileEv->isCancelled()){
|
||||||
|
$ev->getProjectile()->flagForDespawn();
|
||||||
|
return ItemUseResult::fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
$ev->getProjectile()->spawnToAll();
|
||||||
|
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_BOW);
|
||||||
|
}else{
|
||||||
|
$entity->spawnToAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($player->isSurvival()){
|
||||||
|
if(!$infinity){ //TODO: tipped arrows are still consumed when Infinity is applied
|
||||||
|
$player->getInventory()->removeItem(ItemFactory::get(Item::ARROW, 0, 1));
|
||||||
|
}
|
||||||
|
$this->applyDamage(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ItemUseResult::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class Bucket extends Item{
|
|||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{
|
||||||
//TODO: move this to generic placement logic
|
//TODO: move this to generic placement logic
|
||||||
if($blockClicked instanceof Liquid and $blockClicked->isSource()){
|
if($blockClicked instanceof Liquid and $blockClicked->isSource()){
|
||||||
$stack = clone $this;
|
$stack = clone $this;
|
||||||
@ -58,13 +58,12 @@ class Bucket extends Item{
|
|||||||
}else{
|
}else{
|
||||||
$player->getInventory()->addItem($ev->getItem());
|
$player->getInventory()->addItem($ev->getItem());
|
||||||
}
|
}
|
||||||
}else{
|
return ItemUseResult::success();
|
||||||
$player->getInventory()->sendContents($player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return ItemUseResult::fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class FlintSteel extends Tool{
|
|||||||
parent::__construct(self::FLINT_STEEL, 0, "Flint and Steel");
|
parent::__construct(self::FLINT_STEEL, 0, "Flint and Steel");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{
|
||||||
if($blockReplace->getId() === self::AIR){
|
if($blockReplace->getId() === self::AIR){
|
||||||
$level = $player->getLevel();
|
$level = $player->getLevel();
|
||||||
assert($level !== null);
|
assert($level !== null);
|
||||||
@ -44,10 +44,10 @@ class FlintSteel extends Tool{
|
|||||||
|
|
||||||
$this->applyDamage(1);
|
$this->applyDamage(1);
|
||||||
|
|
||||||
return true;
|
return ItemUseResult::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMaxDurability() : int{
|
public function getMaxDurability() : int{
|
||||||
|
@ -735,10 +735,10 @@ class Item implements ItemIds, \JsonSerializable{
|
|||||||
* @param int $face
|
* @param int $face
|
||||||
* @param Vector3 $clickVector
|
* @param Vector3 $clickVector
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return ItemUseResult
|
||||||
*/
|
*/
|
||||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -748,10 +748,10 @@ class Item implements ItemIds, \JsonSerializable{
|
|||||||
* @param Player $player
|
* @param Player $player
|
||||||
* @param Vector3 $directionVector
|
* @param Vector3 $directionVector
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return ItemUseResult
|
||||||
*/
|
*/
|
||||||
public function onClickAir(Player $player, Vector3 $directionVector) : bool{
|
public function onClickAir(Player $player, Vector3 $directionVector) : ItemUseResult{
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -760,10 +760,10 @@ class Item implements ItemIds, \JsonSerializable{
|
|||||||
*
|
*
|
||||||
* @param Player $player
|
* @param Player $player
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return ItemUseResult
|
||||||
*/
|
*/
|
||||||
public function onReleaseUsing(Player $player) : bool{
|
public function onReleaseUsing(Player $player) : ItemUseResult{
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
65
src/pocketmine/item/ItemUseResult.php
Normal file
65
src/pocketmine/item/ItemUseResult.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?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\item;
|
||||||
|
|
||||||
|
final class ItemUseResult{
|
||||||
|
/** @var ItemUseResult */
|
||||||
|
private static $NONE;
|
||||||
|
/** @var ItemUseResult */
|
||||||
|
private static $FAILED;
|
||||||
|
/** @var ItemUseResult */
|
||||||
|
private static $SUCCEEDED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No action attempted to take place. Does nothing.
|
||||||
|
*
|
||||||
|
* @return ItemUseResult
|
||||||
|
*/
|
||||||
|
public static function none() : ItemUseResult{
|
||||||
|
return self::$NONE ?? (self::$NONE = new self());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action attempted to take place, but failed due to cancellation. This triggers rollback behaviour for a remote
|
||||||
|
* player.
|
||||||
|
*
|
||||||
|
* @return ItemUseResult
|
||||||
|
*/
|
||||||
|
public static function fail() : ItemUseResult{
|
||||||
|
return self::$FAILED ?? (self::$FAILED = new self());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action took place successfully. Changes inventory contents if appropriate.
|
||||||
|
*
|
||||||
|
* @return ItemUseResult
|
||||||
|
*/
|
||||||
|
public static function success() : ItemUseResult{
|
||||||
|
return self::$SUCCEEDED ?? (self::$SUCCEEDED = new self());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct(){
|
||||||
|
//NOOP
|
||||||
|
}
|
||||||
|
}
|
@ -52,14 +52,14 @@ class LiquidBucket extends Item{
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{
|
||||||
if(!$blockReplace->canBeReplaced()){
|
if(!$blockReplace->canBeReplaced()){
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: move this to generic placement logic
|
//TODO: move this to generic placement logic
|
||||||
$resultBlock = BlockFactory::get($this->liquidId);
|
$resultBlock = BlockFactory::get($this->liquidId);
|
||||||
if($resultBlock instanceof Liquid){
|
if($resultBlock instanceof Liquid){ //TODO: this should never be false
|
||||||
$ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, ItemFactory::get(Item::BUCKET));
|
$ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, ItemFactory::get(Item::BUCKET));
|
||||||
$ev->call();
|
$ev->call();
|
||||||
if(!$ev->isCancelled()){
|
if(!$ev->isCancelled()){
|
||||||
@ -69,13 +69,12 @@ class LiquidBucket extends Item{
|
|||||||
if($player->isSurvival()){
|
if($player->isSurvival()){
|
||||||
$player->getInventory()->setItemInHand($ev->getItem());
|
$player->getInventory()->setItemInHand($ev->getItem());
|
||||||
}
|
}
|
||||||
}else{
|
return ItemUseResult::success();
|
||||||
$player->getInventory()->sendContents($player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return ItemUseResult::fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,9 @@ class PaintingItem extends Item{
|
|||||||
parent::__construct(self::PAINTING, 0, "Painting");
|
parent::__construct(self::PAINTING, 0, "Painting");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{
|
||||||
if(Facing::axis($face) === Facing::AXIS_Y){
|
if(Facing::axis($face) === Facing::AXIS_Y){
|
||||||
return false;
|
return ItemUseResult::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
$motives = [];
|
$motives = [];
|
||||||
|
@ -53,7 +53,7 @@ abstract class ProjectileItem extends Item{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onClickAir(Player $player, Vector3 $directionVector) : bool{
|
public function onClickAir(Player $player, Vector3 $directionVector) : ItemUseResult{
|
||||||
$nbt = EntityFactory::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch);
|
$nbt = EntityFactory::createBaseNBT($player->add(0, $player->getEyeHeight(), 0), $directionVector, $player->yaw, $player->pitch);
|
||||||
$this->addExtraTags($nbt);
|
$this->addExtraTags($nbt);
|
||||||
|
|
||||||
@ -68,14 +68,15 @@ abstract class ProjectileItem extends Item{
|
|||||||
$projectileEv->call();
|
$projectileEv->call();
|
||||||
if($projectileEv->isCancelled()){
|
if($projectileEv->isCancelled()){
|
||||||
$projectile->flagForDespawn();
|
$projectile->flagForDespawn();
|
||||||
}else{
|
return ItemUseResult::fail();
|
||||||
$projectile->spawnToAll();
|
|
||||||
|
|
||||||
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 0, EntityIds::PLAYER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$projectile->spawnToAll();
|
||||||
|
|
||||||
|
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 0, EntityIds::PLAYER);
|
||||||
|
|
||||||
$this->pop();
|
$this->pop();
|
||||||
|
|
||||||
return true;
|
return ItemUseResult::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ class SpawnEgg extends Item{
|
|||||||
$this->entityClass = $entityClass;
|
$this->entityClass = $entityClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{
|
||||||
$nbt = EntityFactory::createBaseNBT($blockReplace->add(0.5, 0, 0.5), null, lcg_value() * 360, 0);
|
$nbt = EntityFactory::createBaseNBT($blockReplace->add(0.5, 0, 0.5), null, lcg_value() * 360, 0);
|
||||||
|
|
||||||
if($this->hasCustomName()){
|
if($this->hasCustomName()){
|
||||||
@ -60,6 +60,7 @@ class SpawnEgg extends Item{
|
|||||||
$entity = EntityFactory::create($this->entityClass, $player->getLevel(), $nbt);
|
$entity = EntityFactory::create($this->entityClass, $player->getLevel(), $nbt);
|
||||||
$this->pop();
|
$this->pop();
|
||||||
$entity->spawnToAll();
|
$entity->spawnToAll();
|
||||||
return true;
|
//TODO: what if the entity was marked for deletion?
|
||||||
|
return ItemUseResult::success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ use pocketmine\event\level\SpawnChangeEvent;
|
|||||||
use pocketmine\event\player\PlayerInteractEvent;
|
use pocketmine\event\player\PlayerInteractEvent;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\ItemFactory;
|
use pocketmine\item\ItemFactory;
|
||||||
|
use pocketmine\item\ItemUseResult;
|
||||||
use pocketmine\level\biome\Biome;
|
use pocketmine\level\biome\Biome;
|
||||||
use pocketmine\level\format\Chunk;
|
use pocketmine\level\format\Chunk;
|
||||||
use pocketmine\level\format\ChunkException;
|
use pocketmine\level\format\ChunkException;
|
||||||
@ -1798,8 +1799,11 @@ class Level implements ChunkManager, Metadatable{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$player->isSneaking() and $item->onActivate($player, $blockReplace, $blockClicked, $face, $clickVector)){
|
if(!$player->isSneaking()){
|
||||||
return true;
|
$result = $item->onActivate($player, $blockReplace, $blockClicked, $face, $clickVector);
|
||||||
|
if($result !== ItemUseResult::none()){
|
||||||
|
return $result === ItemUseResult::success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user