Rough OffHand implementation

this doesn't do stuff like taking arrows from offhand yet.
This commit is contained in:
Dylan K. Taylor 2021-05-05 14:46:51 +01:00
parent e8cb49f7ae
commit 652de2632a
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
9 changed files with 101 additions and 11 deletions

View File

@ -1599,6 +1599,7 @@ abstract class Entity{
$properties->setLong(EntityMetadataProperties::TARGET_EID, $this->targetId ?? 0);
$properties->setString(EntityMetadataProperties::NAMETAG, $this->nameTag);
$properties->setString(EntityMetadataProperties::SCORE_TAG, $this->scoreTag);
$properties->setByte(EntityMetadataProperties::COLOR, 0);
$properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true);
$properties->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb);

View File

@ -239,6 +239,7 @@ class ExperienceManager{
public function onPickupXp(int $xpValue) : void{
static $mainHandIndex = -1;
static $offHandIndex = -2;
//TODO: replace this with a more generic equipment getting/setting interface
/** @var Durable[] $equipment */
@ -247,7 +248,9 @@ class ExperienceManager{
if(($item = $this->entity->getInventory()->getItemInHand()) instanceof Durable and $item->hasEnchantment(VanillaEnchantments::MENDING())){
$equipment[$mainHandIndex] = $item;
}
//TODO: check offhand
if(($item = $this->entity->getOffHandInventory()->getItem(0)) instanceof Durable and $item->hasEnchantment(VanillaEnchantments::MENDING())){
$equipment[$offHandIndex] = $item;
}
foreach($this->entity->getArmorInventory()->getContents() as $k => $armorItem){
if($armorItem instanceof Durable and $armorItem->hasEnchantment(VanillaEnchantments::MENDING())){
$equipment[$k] = $armorItem;
@ -263,6 +266,8 @@ class ExperienceManager{
if($k === $mainHandIndex){
$this->entity->getInventory()->setItemInHand($repairItem);
}elseif($k === $offHandIndex){
$this->entity->getOffHandInventory()->setItem(0, $repairItem);
}else{
$this->entity->getArmorInventory()->setItem($k, $repairItem);
}

View File

@ -34,6 +34,7 @@ use pocketmine\inventory\Inventory;
use pocketmine\inventory\InventoryHolder;
use pocketmine\inventory\PlayerEnderInventory;
use pocketmine\inventory\PlayerInventory;
use pocketmine\inventory\PlayerOffHandInventory;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
use pocketmine\item\Totem;
@ -73,6 +74,9 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
/** @var PlayerInventory */
protected $inventory;
/** @var PlayerOffHandInventory */
protected $offHandInventory;
/** @var PlayerEnderInventory */
protected $enderInventory;
@ -193,6 +197,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
return $this->inventory;
}
public function getOffHandInventory() : PlayerOffHandInventory{ return $this->offHandInventory; }
public function getEnderInventory() : PlayerEnderInventory{
return $this->enderInventory;
}
@ -218,7 +224,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->inventory = new PlayerInventory($this);
$syncHeldItem = function() : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobEquipmentChange($this);
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
}
};
$this->inventory->getListeners()->add(new CallbackInventoryListener(
@ -233,6 +239,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
}
}
));
$this->offHandInventory = new PlayerOffHandInventory($this);
$this->enderInventory = new PlayerEnderInventory($this);
$this->initHumanData($nbt);
@ -258,6 +265,15 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->armorInventory->getListeners()->add(...$armorListeners);
$this->inventory->getListeners()->add(...$inventoryListeners);
}
$offHand = $nbt->getCompoundTag("OffHandItem");
if($offHand !== null){
$this->offHandInventory->setItem(0, Item::nbtDeserialize($offHand));
}
$this->offHandInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(function() : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobOffHandItemChange($this);
}
}));
$enderChestInventoryTag = $nbt->getListTag("EnderChestInventory");
if($enderChestInventoryTag !== null){
@ -270,7 +286,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
$this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{
foreach($this->getViewers() as $viewer){
$viewer->getNetworkSession()->onMobEquipmentChange($this);
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
}
});
@ -309,7 +325,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$type = $source->getCause();
if($type !== EntityDamageEvent::CAUSE_SUICIDE and $type !== EntityDamageEvent::CAUSE_VOID
and $this->inventory->getItemInHand() instanceof Totem){ //TODO: check offhand as well (when it's implemented)
and ($this->inventory->getItemInHand() instanceof Totem || $this->offHandInventory->getItem(0) instanceof Totem)){
$compensation = $this->getHealth() - $source->getFinalDamage() - 1;
if($compensation < 0){
@ -335,6 +351,9 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
if($hand instanceof Totem){
$hand->pop(); //Plugins could alter max stack size
$this->inventory->setItemInHand($hand);
}elseif(($offHand = $this->offHandInventory->getItem(0)) instanceof Totem){
$offHand->pop();
$this->offHandInventory->setItem(0, $offHand);
}
}
}
@ -342,7 +361,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
public function getDrops() : array{
return array_filter(array_merge(
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : []
$this->armorInventory !== null ? array_values($this->armorInventory->getContents()) : [],
$this->offHandInventory !== null ? array_values($this->offHandInventory->getContents()) : [],
), function(Item $item) : bool{ return !$item->hasEnchantment(VanillaEnchantments::VANISHING()); });
}
@ -381,6 +401,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$nbt->setInt("SelectedInventorySlot", $this->inventory->getHeldItemIndex());
}
$offHandItem = $this->offHandInventory->getItem(0);
if(!$offHandItem->isNull()){
$nbt->setTag("OffHandItem", $offHandItem->nbtSerialize());
}
if($this->enderInventory !== null){
/** @var CompoundTag[] $items */
@ -437,6 +461,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
$player->getNetworkSession()->onMobArmorChange($this);
$player->getNetworkSession()->onMobOffHandItemChange($this);
if(!($this instanceof Player)){
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
@ -466,12 +491,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
protected function onDispose() : void{
$this->inventory->removeAllViewers();
$this->inventory->getHeldItemIndexChangeListeners()->clear();
$this->offHandInventory->removeAllViewers();
$this->enderInventory->removeAllViewers();
parent::onDispose();
}
protected function destroyCycles() : void{
$this->inventory = null;
$this->offHandInventory = null;
$this->enderInventory = null;
$this->hungerManager = null;
$this->xpManager = null;

View File

@ -0,0 +1,38 @@
<?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\inventory;
use pocketmine\entity\Human;
final class PlayerOffHandInventory extends BaseInventory{
/** @var Human */
private $holder;
public function __construct(Human $player){
$this->holder = $player;
parent::__construct(1);
}
public function getHolder() : Human{ return $this->holder; }
}

View File

@ -96,6 +96,7 @@ class InventoryManager{
$this->containerOpenCallbacks->add(\Closure::fromCallable([self::class, 'createContainerOpen']));
$this->add(ContainerIds::INVENTORY, $this->player->getInventory());
$this->add(ContainerIds::OFFHAND, $this->player->getOffHandInventory());
$this->add(ContainerIds::ARMOR, $this->player->getArmorInventory());
$this->add(ContainerIds::UI, $this->player->getCursorInventory());

View File

@ -963,14 +963,18 @@ class NetworkSession{
/**
* TODO: expand this to more than just humans
* TODO: offhand
*/
public function onMobEquipmentChange(Human $mob) : void{
public function onMobMainHandItemChange(Human $mob) : void{
//TODO: we could send zero for slot here because remote players don't need to know which slot was selected
$inv = $mob->getInventory();
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), ContainerIds::INVENTORY));
}
public function onMobOffHandItemChange(Human $mob) : void{
$inv = $mob->getOffHandInventory();
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), 0, ContainerIds::OFFHAND));
}
public function onMobArmorChange(Living $mob) : void{
$inv = $mob->getArmorInventory();
$converter = TypeConverter::getInstance();

View File

@ -463,11 +463,17 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleMobEquipment(MobEquipmentPacket $packet) : bool{
$this->session->getInvManager()->onClientSelectHotbarSlot($packet->hotbarSlot);
if(!$this->player->selectHotbarSlot($packet->hotbarSlot)){
$this->session->getInvManager()->syncSelectedHotbarSlot();
if($packet->windowId === ContainerIds::OFFHAND){
return true; //this happens when we put an item into the offhand
}
return true;
if($packet->windowId === ContainerIds::INVENTORY){
$this->session->getInvManager()->onClientSelectHotbarSlot($packet->hotbarSlot);
if(!$this->player->selectHotbarSlot($packet->hotbarSlot)){
$this->session->getInvManager()->syncSelectedHotbarSlot();
}
return true;
}
return false;
}
public function handleMobArmorEquipment(MobArmorEquipmentPacket $packet) : bool{

View File

@ -2103,6 +2103,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
if($this->armorInventory !== null){
$this->armorInventory->clearAll();
}
if($this->offHandInventory !== null){
$this->offHandInventory->clearAll();
}
}
$this->getWorld()->dropExperience($this->location, $ev->getXpDropAmount());

View File

@ -50,6 +50,11 @@ parameters:
count: 1
path: ../../../src/entity/Human.php
-
message: "#^Property pocketmine\\\\entity\\\\Human\\:\\:\\$offHandInventory \\(pocketmine\\\\inventory\\\\PlayerOffHandInventory\\) does not accept null\\.$#"
count: 1
path: ../../../src/entity/Human.php
-
message: "#^Property pocketmine\\\\entity\\\\Human\\:\\:\\$xpManager \\(pocketmine\\\\entity\\\\ExperienceManager\\) does not accept null\\.$#"
count: 1