mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-04 09:10:00 +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->setLong(EntityMetadataProperties::TARGET_EID, $this->targetId ?? 0);
|
||||||
$properties->setString(EntityMetadataProperties::NAMETAG, $this->nameTag);
|
$properties->setString(EntityMetadataProperties::NAMETAG, $this->nameTag);
|
||||||
$properties->setString(EntityMetadataProperties::SCORE_TAG, $this->scoreTag);
|
$properties->setString(EntityMetadataProperties::SCORE_TAG, $this->scoreTag);
|
||||||
|
$properties->setByte(EntityMetadataProperties::COLOR, 0);
|
||||||
|
|
||||||
$properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true);
|
$properties->setGenericFlag(EntityMetadataFlags::AFFECTED_BY_GRAVITY, true);
|
||||||
$properties->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb);
|
$properties->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, $this->canClimb);
|
||||||
|
@ -239,6 +239,7 @@ class ExperienceManager{
|
|||||||
|
|
||||||
public function onPickupXp(int $xpValue) : void{
|
public function onPickupXp(int $xpValue) : void{
|
||||||
static $mainHandIndex = -1;
|
static $mainHandIndex = -1;
|
||||||
|
static $offHandIndex = -2;
|
||||||
|
|
||||||
//TODO: replace this with a more generic equipment getting/setting interface
|
//TODO: replace this with a more generic equipment getting/setting interface
|
||||||
/** @var Durable[] $equipment */
|
/** @var Durable[] $equipment */
|
||||||
@ -247,7 +248,9 @@ class ExperienceManager{
|
|||||||
if(($item = $this->entity->getInventory()->getItemInHand()) instanceof Durable and $item->hasEnchantment(VanillaEnchantments::MENDING())){
|
if(($item = $this->entity->getInventory()->getItemInHand()) instanceof Durable and $item->hasEnchantment(VanillaEnchantments::MENDING())){
|
||||||
$equipment[$mainHandIndex] = $item;
|
$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){
|
foreach($this->entity->getArmorInventory()->getContents() as $k => $armorItem){
|
||||||
if($armorItem instanceof Durable and $armorItem->hasEnchantment(VanillaEnchantments::MENDING())){
|
if($armorItem instanceof Durable and $armorItem->hasEnchantment(VanillaEnchantments::MENDING())){
|
||||||
$equipment[$k] = $armorItem;
|
$equipment[$k] = $armorItem;
|
||||||
@ -263,6 +266,8 @@ class ExperienceManager{
|
|||||||
|
|
||||||
if($k === $mainHandIndex){
|
if($k === $mainHandIndex){
|
||||||
$this->entity->getInventory()->setItemInHand($repairItem);
|
$this->entity->getInventory()->setItemInHand($repairItem);
|
||||||
|
}elseif($k === $offHandIndex){
|
||||||
|
$this->entity->getOffHandInventory()->setItem(0, $repairItem);
|
||||||
}else{
|
}else{
|
||||||
$this->entity->getArmorInventory()->setItem($k, $repairItem);
|
$this->entity->getArmorInventory()->setItem($k, $repairItem);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ use pocketmine\inventory\Inventory;
|
|||||||
use pocketmine\inventory\InventoryHolder;
|
use pocketmine\inventory\InventoryHolder;
|
||||||
use pocketmine\inventory\PlayerEnderInventory;
|
use pocketmine\inventory\PlayerEnderInventory;
|
||||||
use pocketmine\inventory\PlayerInventory;
|
use pocketmine\inventory\PlayerInventory;
|
||||||
|
use pocketmine\inventory\PlayerOffHandInventory;
|
||||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
use pocketmine\item\Totem;
|
use pocketmine\item\Totem;
|
||||||
@ -73,6 +74,9 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
/** @var PlayerInventory */
|
/** @var PlayerInventory */
|
||||||
protected $inventory;
|
protected $inventory;
|
||||||
|
|
||||||
|
/** @var PlayerOffHandInventory */
|
||||||
|
protected $offHandInventory;
|
||||||
|
|
||||||
/** @var PlayerEnderInventory */
|
/** @var PlayerEnderInventory */
|
||||||
protected $enderInventory;
|
protected $enderInventory;
|
||||||
|
|
||||||
@ -193,6 +197,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
return $this->inventory;
|
return $this->inventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOffHandInventory() : PlayerOffHandInventory{ return $this->offHandInventory; }
|
||||||
|
|
||||||
public function getEnderInventory() : PlayerEnderInventory{
|
public function getEnderInventory() : PlayerEnderInventory{
|
||||||
return $this->enderInventory;
|
return $this->enderInventory;
|
||||||
}
|
}
|
||||||
@ -218,7 +224,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
$this->inventory = new PlayerInventory($this);
|
$this->inventory = new PlayerInventory($this);
|
||||||
$syncHeldItem = function() : void{
|
$syncHeldItem = function() : void{
|
||||||
foreach($this->getViewers() as $viewer){
|
foreach($this->getViewers() as $viewer){
|
||||||
$viewer->getNetworkSession()->onMobEquipmentChange($this);
|
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$this->inventory->getListeners()->add(new CallbackInventoryListener(
|
$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->enderInventory = new PlayerEnderInventory($this);
|
||||||
$this->initHumanData($nbt);
|
$this->initHumanData($nbt);
|
||||||
|
|
||||||
@ -258,6 +265,15 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
$this->armorInventory->getListeners()->add(...$armorListeners);
|
$this->armorInventory->getListeners()->add(...$armorListeners);
|
||||||
$this->inventory->getListeners()->add(...$inventoryListeners);
|
$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");
|
$enderChestInventoryTag = $nbt->getListTag("EnderChestInventory");
|
||||||
if($enderChestInventoryTag !== null){
|
if($enderChestInventoryTag !== null){
|
||||||
@ -270,7 +286,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
|
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
|
||||||
$this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{
|
$this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{
|
||||||
foreach($this->getViewers() as $viewer){
|
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();
|
$type = $source->getCause();
|
||||||
if($type !== EntityDamageEvent::CAUSE_SUICIDE and $type !== EntityDamageEvent::CAUSE_VOID
|
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;
|
$compensation = $this->getHealth() - $source->getFinalDamage() - 1;
|
||||||
if($compensation < 0){
|
if($compensation < 0){
|
||||||
@ -335,6 +351,9 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
if($hand instanceof Totem){
|
if($hand instanceof Totem){
|
||||||
$hand->pop(); //Plugins could alter max stack size
|
$hand->pop(); //Plugins could alter max stack size
|
||||||
$this->inventory->setItemInHand($hand);
|
$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{
|
public function getDrops() : array{
|
||||||
return array_filter(array_merge(
|
return array_filter(array_merge(
|
||||||
$this->inventory !== null ? array_values($this->inventory->getContents()) : [],
|
$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()); });
|
), 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());
|
$nbt->setInt("SelectedInventorySlot", $this->inventory->getHeldItemIndex());
|
||||||
}
|
}
|
||||||
|
$offHandItem = $this->offHandInventory->getItem(0);
|
||||||
|
if(!$offHandItem->isNull()){
|
||||||
|
$nbt->setTag("OffHandItem", $offHandItem->nbtSerialize());
|
||||||
|
}
|
||||||
|
|
||||||
if($this->enderInventory !== null){
|
if($this->enderInventory !== null){
|
||||||
/** @var CompoundTag[] $items */
|
/** @var CompoundTag[] $items */
|
||||||
@ -437,6 +461,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
|
$this->sendData([$player], [EntityMetadataProperties::NAMETAG => new StringMetadataProperty($this->getNameTag())]);
|
||||||
|
|
||||||
$player->getNetworkSession()->onMobArmorChange($this);
|
$player->getNetworkSession()->onMobArmorChange($this);
|
||||||
|
$player->getNetworkSession()->onMobOffHandItemChange($this);
|
||||||
|
|
||||||
if(!($this instanceof Player)){
|
if(!($this instanceof Player)){
|
||||||
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
|
$player->getNetworkSession()->sendDataPacket(PlayerListPacket::remove([PlayerListEntry::createRemovalEntry($this->uuid)]));
|
||||||
@ -466,12 +491,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
protected function onDispose() : void{
|
protected function onDispose() : void{
|
||||||
$this->inventory->removeAllViewers();
|
$this->inventory->removeAllViewers();
|
||||||
$this->inventory->getHeldItemIndexChangeListeners()->clear();
|
$this->inventory->getHeldItemIndexChangeListeners()->clear();
|
||||||
|
$this->offHandInventory->removeAllViewers();
|
||||||
$this->enderInventory->removeAllViewers();
|
$this->enderInventory->removeAllViewers();
|
||||||
parent::onDispose();
|
parent::onDispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function destroyCycles() : void{
|
protected function destroyCycles() : void{
|
||||||
$this->inventory = null;
|
$this->inventory = null;
|
||||||
|
$this->offHandInventory = null;
|
||||||
$this->enderInventory = null;
|
$this->enderInventory = null;
|
||||||
$this->hungerManager = null;
|
$this->hungerManager = null;
|
||||||
$this->xpManager = 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->containerOpenCallbacks->add(\Closure::fromCallable([self::class, 'createContainerOpen']));
|
||||||
|
|
||||||
$this->add(ContainerIds::INVENTORY, $this->player->getInventory());
|
$this->add(ContainerIds::INVENTORY, $this->player->getInventory());
|
||||||
|
$this->add(ContainerIds::OFFHAND, $this->player->getOffHandInventory());
|
||||||
$this->add(ContainerIds::ARMOR, $this->player->getArmorInventory());
|
$this->add(ContainerIds::ARMOR, $this->player->getArmorInventory());
|
||||||
$this->add(ContainerIds::UI, $this->player->getCursorInventory());
|
$this->add(ContainerIds::UI, $this->player->getCursorInventory());
|
||||||
|
|
||||||
|
@ -963,14 +963,18 @@ class NetworkSession{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: expand this to more than just humans
|
* 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
|
//TODO: we could send zero for slot here because remote players don't need to know which slot was selected
|
||||||
$inv = $mob->getInventory();
|
$inv = $mob->getInventory();
|
||||||
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), ContainerIds::INVENTORY));
|
$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{
|
public function onMobArmorChange(Living $mob) : void{
|
||||||
$inv = $mob->getArmorInventory();
|
$inv = $mob->getArmorInventory();
|
||||||
$converter = TypeConverter::getInstance();
|
$converter = TypeConverter::getInstance();
|
||||||
|
@ -463,12 +463,18 @@ class InGamePacketHandler extends PacketHandler{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function handleMobEquipment(MobEquipmentPacket $packet) : bool{
|
public function handleMobEquipment(MobEquipmentPacket $packet) : bool{
|
||||||
|
if($packet->windowId === ContainerIds::OFFHAND){
|
||||||
|
return true; //this happens when we put an item into the offhand
|
||||||
|
}
|
||||||
|
if($packet->windowId === ContainerIds::INVENTORY){
|
||||||
$this->session->getInvManager()->onClientSelectHotbarSlot($packet->hotbarSlot);
|
$this->session->getInvManager()->onClientSelectHotbarSlot($packet->hotbarSlot);
|
||||||
if(!$this->player->selectHotbarSlot($packet->hotbarSlot)){
|
if(!$this->player->selectHotbarSlot($packet->hotbarSlot)){
|
||||||
$this->session->getInvManager()->syncSelectedHotbarSlot();
|
$this->session->getInvManager()->syncSelectedHotbarSlot();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function handleMobArmorEquipment(MobArmorEquipmentPacket $packet) : bool{
|
public function handleMobArmorEquipment(MobArmorEquipmentPacket $packet) : bool{
|
||||||
return true; //Not used
|
return true; //Not used
|
||||||
|
@ -2103,6 +2103,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
|||||||
if($this->armorInventory !== null){
|
if($this->armorInventory !== null){
|
||||||
$this->armorInventory->clearAll();
|
$this->armorInventory->clearAll();
|
||||||
}
|
}
|
||||||
|
if($this->offHandInventory !== null){
|
||||||
|
$this->offHandInventory->clearAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getWorld()->dropExperience($this->location, $ev->getXpDropAmount());
|
$this->getWorld()->dropExperience($this->location, $ev->getXpDropAmount());
|
||||||
|
@ -50,6 +50,11 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: ../../../src/entity/Human.php
|
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\\.$#"
|
message: "#^Property pocketmine\\\\entity\\\\Human\\:\\:\\$xpManager \\(pocketmine\\\\entity\\\\ExperienceManager\\) does not accept null\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user