Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Shield (wip) #6296

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ private function register1to1ItemMappings() : void{
$this->map1to1Item(Ids::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SHAPER_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SHEARS, Items::SHEARS());
$this->map1to1Item(Ids::SHIELD, Items::SHIELD());
$this->map1to1Item(Ids::SHULKER_SHELL, Items::SHULKER_SHELL());
$this->map1to1Item(Ids::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SILENCE_ARMOR_TRIM_SMITHING_TEMPLATE());
$this->map1to1Item(Ids::SLIME_BALL, Items::SLIMEBALL());
Expand Down
35 changes: 35 additions & 0 deletions src/entity/Human.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
use pocketmine\item\enchantment\EnchantingHelper;
use pocketmine\item\enchantment\VanillaEnchantments;
use pocketmine\item\Item;
use pocketmine\item\Shield;
use pocketmine\item\Totem;
use pocketmine\math\Vector3;
use pocketmine\nbt\NBT;
Expand Down Expand Up @@ -67,6 +68,7 @@
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
use pocketmine\network\mcpe\protocol\UpdateAbilitiesPacket;
use pocketmine\player\Player;
use pocketmine\world\sound\ItemBreakSound;
use pocketmine\world\sound\TotemUseSound;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
Expand All @@ -75,6 +77,8 @@
use function array_key_exists;
use function array_merge;
use function array_values;
use function floor;
use function max;
use function min;

class Human extends Living implements ProjectileSource, InventoryHolder{
Expand Down Expand Up @@ -546,4 +550,35 @@
);
parent::destroyCycles();
}

protected function onBlock(Entity $entity, EntityDamageEvent $e) : void{
parent::onBlock($entity, $e);

$shield = $this->getInventory()->getItemInHand();
$shieldOffhand = $this->getOffhandInventory()->getItem(0);

Check failure on line 558 in src/entity/Human.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

Call to method pocketmine\entity\Human::getOffHandInventory() with incorrect case: getOffhandInventory

Check failure on line 558 in src/entity/Human.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

Call to method pocketmine\entity\Human::getOffHandInventory() with incorrect case: getOffhandInventory

Check failure on line 558 in src/entity/Human.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

Call to method pocketmine\entity\Human::getOffHandInventory() with incorrect case: getOffhandInventory
pandaaaBE marked this conversation as resolved.
Show resolved Hide resolved

if($shield instanceof Shield){
$dShield = $this->damageShield($shield, $e->getFinalDamage());
if($dShield !== null){
$this->getInventory()->setItemInHand($dShield);
}
}elseif($shieldOffhand instanceof Shield){
$dShield = $this->damageShield($shieldOffhand, $e->getFinalDamage());
if($dShield !== null){
$this->getOffHandInventory()->setItem(0, $dShield);
}
}
}

public function damageShield(Shield $item, float $damage) : ?Item{
$durabilityRemoved = (int) max(floor($damage / 4), 1);

$item->applyDamage($durabilityRemoved);
if($item->isBroken()){
$this->broadcastSound(new ItemBreakSound());
return null;
}

return $item;
}
}
108 changes: 89 additions & 19 deletions src/entity/Living.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\EffectManager;
use pocketmine\entity\effect\VanillaEffects;
use pocketmine\event\entity\EntityDamageBlockedEvent;
use pocketmine\event\entity\EntityDamageByChildEntityEvent;
use pocketmine\event\entity\EntityDamageByEntityEvent;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\EntityDeathEvent;
use pocketmine\inventory\ArmorInventory;
use pocketmine\inventory\CallbackInventoryListener;
use pocketmine\inventory\Inventory;
use pocketmine\item\Armor;
use pocketmine\item\Durable;
use pocketmine\item\enchantment\Enchantment;
Expand All @@ -63,6 +63,7 @@
use pocketmine\world\sound\EntityLongFallSound;
use pocketmine\world\sound\EntityShortFallSound;
use pocketmine\world\sound\ItemBreakSound;
use pocketmine\world\sound\ShieldBlockSound;
use function array_shift;
use function atan2;
use function ceil;
Expand Down Expand Up @@ -126,6 +127,10 @@ abstract class Living extends Entity{
protected bool $gliding = false;
protected bool $swimming = false;

private bool $isAttackTimeByShieldKb = false;
private int $attackTimeBefore = 20;
private bool $isBlocking = false;

protected function getInitialDragMultiplier() : float{ return 0.02; }

protected function getInitialGravity() : float{ return 0.08; }
Expand All @@ -149,24 +154,6 @@ protected function initEntity(CompoundTag $nbt) : void{
$this->getViewers(),
fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this)
)));
$playArmorSound = function(Item $newItem, Item $oldItem) : void{
if(!$newItem->isNull() && $newItem instanceof Armor && !$newItem->equalsExact($oldItem)){
$equipSound = $newItem->getMaterial()->getEquipSound();
if($equipSound !== null){
$this->broadcastSound($equipSound);
}
}
};
$this->armorInventory->getListeners()->add(new CallbackInventoryListener(
function(Inventory $inventory, int $slot, Item $oldItem) use ($playArmorSound) : void{
$playArmorSound($inventory->getItem($slot), $oldItem);
},
function(Inventory $inventory, array $oldContents) use ($playArmorSound) : void{
foreach($oldContents as $slot => $oldItem){
$playArmorSound($inventory->getItem($slot), $oldItem);
}
}
));

$health = $this->getMaxHealth();

Expand Down Expand Up @@ -251,6 +238,8 @@ public function setSneaking(bool $value = true) : void{
$this->sneaking = $value;
$this->networkPropertiesDirty = true;
$this->recalculateSize();

$this->setBlocking($value);
}

public function isSprinting() : bool{
Expand Down Expand Up @@ -542,6 +531,15 @@ private function damageItem(Durable $item, int $durabilityRemoved) : void{
public function attack(EntityDamageEvent $source) : void{
if($this->noDamageTicks > 0 && $source->getCause() !== EntityDamageEvent::CAUSE_SUICIDE){
$source->cancel();
}elseif($this->attackTime > 0 && !$this->isAttackTimeByShieldKb){
$lastCause = $this->getLastDamageCause();
if($lastCause !== null && $lastCause->getFinalDamage() >= $source->getFinalDamage()){
$source->cancel();
}
}

if($this->isBlocking && $this->blockedByShield($source)){
$source->cancel();
}

if($this->effectManager->has(VanillaEffects::FIRE_RESISTANCE()) && (
Expand Down Expand Up @@ -574,6 +572,7 @@ public function attack(EntityDamageEvent $source) : void{
}

$this->attackTime = $source->getAttackCooldown();
$this->isAttackTimeByShieldKb = false;

if($source instanceof EntityDamageByChildEntityEvent){
$e = $source->getChild();
Expand Down Expand Up @@ -685,10 +684,14 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{
}
}
}

pandaaaBE marked this conversation as resolved.
Show resolved Hide resolved
}

if($this->attackTime > 0){
$this->attackTime -= $tickDiff;
if($this->attackTime <= 0){
$this->isAttackTimeByShieldKb = false;
}
}

Timings::$livingEntityBaseTick->stopTiming();
Expand Down Expand Up @@ -904,6 +907,7 @@ protected function syncNetworkData(EntityMetadataCollection $properties) : void{
$properties->setGenericFlag(EntityMetadataFlags::SPRINTING, $this->sprinting);
$properties->setGenericFlag(EntityMetadataFlags::GLIDING, $this->gliding);
$properties->setGenericFlag(EntityMetadataFlags::SWIMMING, $this->swimming);
$properties->setGenericFlag(EntityMetadataFlags::BLOCKING, $this->isBlocking);
}

protected function onDispose() : void{
Expand All @@ -920,4 +924,70 @@ protected function destroyCycles() : void{
);
parent::destroyCycles();
}

protected function blockedByShield(EntityDamageEvent $source) : bool{
$damager = null;
if($source instanceof EntityDamageByChildEntityEvent){
$damager = $source->getChild();
}elseif($source instanceof EntityDamageByEntityEvent){
$damager = $source->getDamager();
}

if($damager === null || !$this->isBlocking()){
return false;
}

$entityPos = $damager->getPosition();
$direction = $this->getDirectionVector();
$normalizedVector = $this->getPosition()->subtractVector($entityPos)->normalize();

$blocked = ($normalizedVector->x * $direction->x) + ($normalizedVector->z * $direction->z) < 0.0;

$event = new EntityDamageBlockedEvent($damager, $this, $source->getFinalDamage());

if(!$blocked || !$source->canBeReducedByArmor()){
$event->cancel();
}

if($event->isCancelled()){
return false;
}

if($damager instanceof Living){
$deltaX = $damager->getPosition()->x - $this->getPosition()->x;
$deltaZ = $damager->getPosition()->z - $this->getPosition()->z;
$damager->knockBack($deltaX, $deltaZ);
$damager->attackTime = 10;
$damager->isAttackTimeByShieldKb = true;
pandaaaBE marked this conversation as resolved.
Show resolved Hide resolved
}

$this->onBlock($damager, $source);
return true;
}

protected function onBlock(Entity $entity, EntityDamageEvent $e) : void{
$this->getWorld()->addSound($entity->getPosition(), new ShieldBlockSound());
}

public function isBlocking() : bool{
return $this->isBlocking;
}

public function setBlocking(bool $value) : void{
$this->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::BLOCKING, $value);
$this->isBlocking = $value;
}

public function preAttack(Player $player) : void{
pandaaaBE marked this conversation as resolved.
Show resolved Hide resolved
if($this->isAttackTimeByShieldKb){
$this->attackTimeBefore = $this->attackTime;
$this->attackTime = 0;
}
}

public function postAttack(Player $player) : void{
pandaaaBE marked this conversation as resolved.
Show resolved Hide resolved
if($this->isAttackTimeByShieldKb && $this->attackTime == 0){
$this->attackTime = $this->attackTimeBefore;
}
}
}
49 changes: 49 additions & 0 deletions src/event/entity/EntityDamageBlockedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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\event\entity;

use pocketmine\entity\Entity;
use pocketmine\event\CancellableTrait;
/**
* @phpstan-extends EntityDamageByEntityEvent<Entity>
*/
class EntityDamageBlockedEvent extends EntityDamageByEntityEvent{

Check failure on line 31 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

PHPDoc tag @extends contains generic type pocketmine\event\entity\EntityDamageByEntityEvent<pocketmine\entity\Entity> but class pocketmine\event\entity\EntityDamageByEntityEvent is not generic.

Check failure on line 31 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

PHPDoc tag @extends contains generic type pocketmine\event\entity\EntityDamageByEntityEvent<pocketmine\entity\Entity> but class pocketmine\event\entity\EntityDamageByEntityEvent is not generic.

Check failure on line 31 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

PHPDoc tag @extends contains generic type pocketmine\event\entity\EntityDamageByEntityEvent<pocketmine\entity\Entity> but class pocketmine\event\entity\EntityDamageByEntityEvent is not generic.
pandaaaBE marked this conversation as resolved.
Show resolved Hide resolved
use CancellableTrait;

protected Entity $entity;
private Entity $damager;

Check failure on line 35 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

Property pocketmine\event\entity\EntityDamageBlockedEvent::$damager is never read, only written.

Check failure on line 35 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

Property pocketmine\event\entity\EntityDamageBlockedEvent::$damager is never read, only written.

Check failure on line 35 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

Property pocketmine\event\entity\EntityDamageBlockedEvent::$damager is never read, only written.
private float $damage;

Check failure on line 36 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

Property pocketmine\event\entity\EntityDamageBlockedEvent::$damage is never read, only written.

Check failure on line 36 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

Property pocketmine\event\entity\EntityDamageBlockedEvent::$damage is never read, only written.

Check failure on line 36 in src/event/entity/EntityDamageBlockedEvent.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

Property pocketmine\event\entity\EntityDamageBlockedEvent::$damage is never read, only written.

public function __construct(
Entity $damager,
Entity $entity,
float $damage,
){
$this->entity = $entity;
$this->damager = $damager;
$this->damage = $damage;

parent::__construct($damager, $entity, EntityDamageEvent::CAUSE_BLOCKING, $damage);
}
}
20 changes: 20 additions & 0 deletions src/event/entity/EntityDamageEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
public const CAUSE_CUSTOM = 14;
public const CAUSE_STARVATION = 15;
public const CAUSE_FALLING_BLOCK = 16;
public const CAUSE_BLOCKING = 17;

private float $baseDamage;
private float $originalBase;
Expand All @@ -73,6 +74,9 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
private array $originals;
private int $attackCooldown = 10;

private bool $breakShield = false;
private int $shieldBreakCooldown = 100;

/**
* @param float[] $modifiers
*/
Expand Down Expand Up @@ -183,4 +187,20 @@ public function getAttackCooldown() : int{
public function setAttackCooldown(int $attackCooldown) : void{
$this->attackCooldown = $attackCooldown;
}

public function isBreakShield() : bool{
return $this->breakShield;
}

public function setBreakShield(bool $value) : void{
$this->breakShield = $value;
}

public function getBreakShieldCooldown() : int{
return $this->shieldBreakCooldown;
}

public function setBreakShieldCooldown(int $cooldown) : void{
$this->shieldBreakCooldown = $cooldown;
}
}
4 changes: 4 additions & 0 deletions src/item/Axe.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public function getAttackPoints() : int{
return $this->tier->getBaseAttackPoints() - 1;
}

public function canBreakShield() : bool{
return true;
}

public function onDestroyBlock(Block $block, array &$returnedItems) : bool{
if(!$block->getBreakInfo()->breaksInstantly()){
return $this->applyDamage(1);
Expand Down
7 changes: 7 additions & 0 deletions src/item/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,13 @@ public function getAttackPoints() : int{
return 1;
}

/**
* Returns if item can break the shield.
*/
public function canBreakShield() : bool{
return false;
}

/**
* Returns how many armor points can be gained by wearing this item.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/item/ItemTypeIds.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,9 @@ private function __construct(){
public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = 20285;
public const PITCHER_POD = 20286;
public const NAME_TAG = 20287;
public const SHIELD = 20288;

public const FIRST_UNUSED_ITEM_ID = 20288;
public const FIRST_UNUSED_ITEM_ID = 20289;

private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

Expand Down
Loading
Loading