Moved bow functionality out of Player

This commit is contained in:
Dylan K. Taylor 2017-08-26 19:04:04 +01:00
parent 6d5620606e
commit a5f5502380
3 changed files with 123 additions and 68 deletions

View File

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

View File

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

View File

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