mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Rough OffHand implementation
this doesn't do stuff like taking arrows from offhand yet.
This commit is contained in:
parent
e8cb49f7ae
commit
652de2632a
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
38
src/inventory/PlayerOffhandInventory.php
Normal file
38
src/inventory/PlayerOffhandInventory.php
Normal 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; }
|
||||
}
|
@ -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());
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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{
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user