From a5f5502380d713e74cff4735883954e5f5727dcc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 26 Aug 2017 19:04:04 +0100 Subject: [PATCH] Moved bow functionality out of Player --- src/pocketmine/Player.php | 95 ++++++++++-------------------------- src/pocketmine/item/Bow.php | 86 ++++++++++++++++++++++++++++++++ src/pocketmine/item/Item.php | 10 ++++ 3 files changed, 123 insertions(+), 68 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index e06be33f7..e328aed61 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -38,7 +38,6 @@ use pocketmine\entity\Projectile; use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\entity\EntityShootBowEvent; use pocketmine\event\entity\ProjectileLaunchEvent; use pocketmine\event\inventory\CraftItemEvent; use pocketmine\event\inventory\InventoryCloseEvent; @@ -103,7 +102,6 @@ use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\LongTag; -use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\StringTag; use pocketmine\network\mcpe\PlayerNetworkSessionAdapter; use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; @@ -760,6 +758,29 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return $this->inAirTicks; } + /** + * Returns whether the player is currently using an item (right-click and hold). + * @return bool + */ + public function isUsingItem() : bool{ + return $this->getGenericFlag(self::DATA_FLAG_ACTION) and $this->startAction > -1; + } + + public function setUsingItem(bool $value){ + $this->startAction = $value ? $this->server->getTick() : -1; + $this->setGenericFlag(self::DATA_FLAG_ACTION, $value); + } + + /** + * Returns how long the player has been using their currently-held item for. Used for determining arrow shoot force + * for bows. + * + * @return int + */ + public function getItemUseDuration() : int{ + return $this->startAction === -1 ? -1 : ($this->server->getTick() - $this->startAction); + } + protected function switchLevel(Level $targetLevel) : bool{ $oldLevel = $this->level; if(parent::switchLevel($targetLevel)){ @@ -2470,72 +2491,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_BLOCK_STOP_BREAK); break; case PlayerActionPacket::ACTION_RELEASE_ITEM: - if($this->startAction > -1 and $this->getGenericFlag(self::DATA_FLAG_ACTION)){ - if($this->inventory->getItemInHand()->getId() === Item::BOW){ - $bow = $this->inventory->getItemInHand(); - if($this->isSurvival() and !$this->inventory->contains(ItemFactory::get(Item::ARROW, 0, 1))){ - $this->inventory->sendContents($this); - break; - } - - $nbt = new CompoundTag("", [ - new ListTag("Pos", [ - new DoubleTag("", $this->x), - new DoubleTag("", $this->y + $this->getEyeHeight()), - new DoubleTag("", $this->z) - ]), - new ListTag("Motion", [ - new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), - new DoubleTag("", -sin($this->pitch / 180 * M_PI)), - new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) - ]), - new ListTag("Rotation", [ - //yaw/pitch for arrows taken crosswise, not along the arrow shaft. - new FloatTag("", ($this->yaw > 180 ? 360 : 0) - $this->yaw), //arrow yaw must range from -180 to +180 - new FloatTag("", -$this->pitch) - ]), - new ShortTag("Fire", $this->isOnFire() ? 45 * 60 : 0) - ]); - - $diff = ($this->server->getTick() - $this->startAction); - $p = $diff / 20; - $f = min((($p ** 2) + $p * 2) / 3, 1) * 2; - $ev = new EntityShootBowEvent($this, $bow, Entity::createEntity("Arrow", $this->getLevel(), $nbt, $this, $f == 2), $f); - - if($f < 0.1 or $diff < 5){ - $ev->setCancelled(); - } - - $this->server->getPluginManager()->callEvent($ev); - - if($ev->isCancelled()){ - $ev->getProjectile()->kill(); - $this->inventory->sendContents($this); - }else{ - $ev->getProjectile()->setMotion($ev->getProjectile()->getMotion()->multiply($ev->getForce())); - if($this->isSurvival()){ - $this->inventory->removeItem(ItemFactory::get(Item::ARROW, 0, 1)); - $bow->setDamage($bow->getDamage() + 1); - if($bow->getDamage() >= 385){ - $this->inventory->setItemInHand(ItemFactory::get(Item::AIR, 0, 0)); - }else{ - $this->inventory->setItemInHand($bow); - } - } - - $projectile = $ev->getProjectile(); - if($projectile instanceof Projectile){ - $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($projectile)); - if($projectileEv->isCancelled()){ - $ev->getProjectile()->kill(); - }else{ - $ev->getProjectile()->spawnToAll(); - $this->level->addSound(new LaunchSound($this), $this->getViewers()); - } - }else{ - $ev->getProjectile()->spawnToAll(); - } - } + if($this->isUsingItem()){ + $item = $this->inventory->getItemInHand(); + if($item->onReleaseUsing($this)){ + $this->inventory->setItemInHand($item); } }elseif($this->inventory->getItemInHand()->getId() === Item::BUCKET and $this->inventory->getItemInHand()->getDamage() === 1){ //Milk! $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $this->inventory->getItemInHand())); diff --git a/src/pocketmine/item/Bow.php b/src/pocketmine/item/Bow.php index 7fe68608c..41e019eb5 100644 --- a/src/pocketmine/item/Bow.php +++ b/src/pocketmine/item/Bow.php @@ -23,6 +23,17 @@ declare(strict_types=1); namespace pocketmine\item; +use pocketmine\entity\Entity; +use pocketmine\entity\Projectile; +use pocketmine\event\entity\EntityShootBowEvent; +use pocketmine\event\entity\ProjectileLaunchEvent; +use pocketmine\level\sound\LaunchSound; +use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\tag\DoubleTag; +use pocketmine\nbt\tag\FloatTag; +use pocketmine\nbt\tag\ListTag; +use pocketmine\nbt\tag\ShortTag; +use pocketmine\Player; class Bow extends Tool{ public function __construct(int $meta = 0){ @@ -36,4 +47,79 @@ class Bow extends Tool{ public function getMaxDurability(){ return 385; } + + public function onReleaseUsing(Player $player) : bool{ + if($player->isSurvival() and !$player->getInventory()->contains(ItemFactory::get(Item::ARROW, 0, 1))){ + $player->getInventory()->sendContents($player); + } + + $nbt = new CompoundTag("", [ + new ListTag("Pos", [ + new DoubleTag("", $player->x), + new DoubleTag("", $player->y + $player->getEyeHeight()), + new DoubleTag("", $player->z) + ]), + new ListTag("Motion", [ + new DoubleTag("", -sin($player->yaw / 180 * M_PI) * cos($player->pitch / 180 * M_PI)), + new DoubleTag("", -sin($player->pitch / 180 * M_PI)), + new DoubleTag("", cos($player->yaw / 180 * M_PI) * cos($player->pitch / 180 * M_PI)) + ]), + new ListTag("Rotation", [ + //yaw/pitch for arrows taken crosswise, not along the arrow shaft. + new FloatTag("", ($player->yaw > 180 ? 360 : 0) - $player->yaw), //arrow yaw must range from -180 to +180 + new FloatTag("", -$player->pitch) + ]), + new ShortTag("Fire", $player->isOnFire() ? 45 * 60 : 0) + ]); + + $diff = $player->getItemUseDuration(); + $p = $diff / 20; + $force = min((($p ** 2) + $p * 2) / 3, 1) * 2; + + + $entity = Entity::createEntity("Arrow", $player->getLevel(), $nbt, $player, $force == 2); + if($entity instanceof Projectile){ + $ev = new EntityShootBowEvent($player, $this, $entity, $force); + + if($force < 0.1 or $diff < 5){ + $ev->setCancelled(); + } + + $player->getServer()->getPluginManager()->callEvent($ev); + + $entity = $ev->getProjectile(); //This might have been changed by plugins + + if($ev->isCancelled()){ + $entity->kill(); + $player->getInventory()->sendContents($player); + }else{ + $entity->setMotion($entity->getMotion()->multiply($ev->getForce())); + if($player->isSurvival()){ + $player->getInventory()->removeItem(ItemFactory::get(Item::ARROW, 0, 1)); + $this->setDamage($this->getDamage() + 1); + if($this->getDamage() >= $this->getMaxDurability()){ + $player->getInventory()->setItemInHand(ItemFactory::get(Item::AIR, 0, 0)); + }else{ + $player->getInventory()->setItemInHand($this); + } + } + + if($entity instanceof Projectile){ + $player->getServer()->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($entity)); + if($projectileEv->isCancelled()){ + $ev->getProjectile()->kill(); + }else{ + $ev->getProjectile()->spawnToAll(); + $player->level->addSound(new LaunchSound($player), $player->getViewers()); + } + }else{ + $entity->spawnToAll(); + } + } + }else{ + $entity->spawnToAll(); + } + + return true; + } } \ No newline at end of file diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index c64aa463b..c5fca5f9f 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -829,6 +829,16 @@ class Item implements ItemIds, \JsonSerializable{ return false; } + /** + * Called when a player is using this item and releases it. Used to handle bow shoot actions. + * + * @param Player $player + * @return bool + */ + public function onReleaseUsing(Player $player) : bool{ + return false; + } + /** * Compares an Item to this Item and check if they match. *