diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 6202e9ee48..dc9e133a9a 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -34,6 +34,7 @@ use pocketmine\inventory\ArmorInventory; use pocketmine\inventory\ArmorInventoryEventProcessor; use pocketmine\item\Armor; use pocketmine\item\Consumable; +use pocketmine\item\Durable; use pocketmine\item\enchantment\Enchantment; use pocketmine\item\Item; use pocketmine\math\Vector3; @@ -476,6 +477,26 @@ abstract class Living extends Entity implements Damageable{ protected function applyPostDamageEffects(EntityDamageEvent $source) : void{ $this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(EntityDamageEvent::MODIFIER_ABSORPTION))); $this->damageArmor($source->getBaseDamage()); + + if($source instanceof EntityDamageByEntityEvent){ + $damage = 0; + foreach($this->armorInventory->getContents() as $k => $item){ + if($item instanceof Armor and ($thornsLevel = $item->getEnchantmentLevel(Enchantment::THORNS)) > 0){ + if(mt_rand(0, 99) < $thornsLevel * 15){ + $this->damageItem($item, 3); + $damage += ($thornsLevel > 10 ? $thornsLevel - 10 : 1 + mt_rand(0, 3)); + }else{ + $this->damageItem($item, 1); //thorns causes an extra +1 durability loss even if it didn't activate + } + + $this->armorInventory->setItem($k, $item); + } + } + + if($damage > 0){ + $source->getDamager()->attack(new EntityDamageByEntityEvent($this, $source->getDamager(), EntityDamageEvent::CAUSE_MAGIC, $damage)); + } + } } /** @@ -490,16 +511,20 @@ abstract class Living extends Entity implements Damageable{ $armor = $this->armorInventory->getContents(true); foreach($armor as $item){ if($item instanceof Armor){ - $item->applyDamage($durabilityRemoved); - if($item->isBroken()){ - $this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_BREAK); - } + $this->damageItem($item, $durabilityRemoved); } } $this->armorInventory->setContents($armor); } + private function damageItem(Durable $item, int $durabilityRemoved) : void{ + $item->applyDamage($durabilityRemoved); + if($item->isBroken()){ + $this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_BREAK); + } + } + public function attack(EntityDamageEvent $source) : void{ if($this->attackTime > 0 or $this->noDamageTicks > 0){ $lastCause = $this->getLastDamageCause(); @@ -577,14 +602,17 @@ abstract class Living extends Entity implements Damageable{ $motion = clone $this->motion; $motion->x /= 2; - $motion->y /= 2; $motion->z /= 2; $motion->x += $x * $f * $base; - $motion->y += $base; $motion->z += $z * $f * $base; - if($motion->y > $base){ - $motion->y = $base; + if($this->onGround){ + $motion->y /= 2; + $motion->y += $base; + + if($motion->y > 0.4){ //this is hardcoded in vanilla + $motion->y = 0.4; + } } $this->setMotion($motion); diff --git a/src/pocketmine/entity/object/ExperienceOrb.php b/src/pocketmine/entity/object/ExperienceOrb.php index c73edb4148..729f9509c3 100644 --- a/src/pocketmine/entity/object/ExperienceOrb.php +++ b/src/pocketmine/entity/object/ExperienceOrb.php @@ -165,7 +165,7 @@ class ExperienceOrb extends Entity{ } $currentTarget = $this->getTargetPlayer(); - if($currentTarget !== null and $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2){ + if($currentTarget !== null and (!$currentTarget->isAlive() or $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2)){ $currentTarget = null; } diff --git a/src/pocketmine/entity/projectile/Arrow.php b/src/pocketmine/entity/projectile/Arrow.php index 5842149a96..3db4e26a50 100644 --- a/src/pocketmine/entity/projectile/Arrow.php +++ b/src/pocketmine/entity/projectile/Arrow.php @@ -31,6 +31,7 @@ use pocketmine\item\Item; use pocketmine\item\ItemFactory; use pocketmine\level\Level; use pocketmine\math\RayTraceResult; +use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\EntityEventPacket; use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; @@ -58,6 +59,9 @@ class Arrow extends Projectile{ /** @var int */ protected $pickupMode = self::PICKUP_ANY; + /** @var float */ + protected $punchKnockback = 0.0; + public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){ parent::__construct($level, $nbt, $shootingEntity); $this->setCritical($critical); @@ -92,6 +96,20 @@ class Arrow extends Projectile{ } } + /** + * @return float + */ + public function getPunchKnockback() : float{ + return $this->punchKnockback; + } + + /** + * @param float $punchKnockback + */ + public function setPunchKnockback(float $punchKnockback) : void{ + $this->punchKnockback = $punchKnockback; + } + public function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; @@ -117,6 +135,16 @@ class Arrow extends Projectile{ $this->broadcastEntityEvent(EntityEventPacket::ARROW_SHAKE, 7); //7 ticks } + protected function onHitEntity(Entity $entityHit, RayTraceResult $hitResult) : void{ + parent::onHitEntity($entityHit, $hitResult); + if($this->punchKnockback > 0){ + $mot = $entityHit->getMotion(); + $multiplier = $this->punchKnockback * 0.6 / $mot->length(); + + $entityHit->setMotion($mot->add($mot->x * $multiplier, 0.1, $mot->z * $multiplier)); + } + } + /** * @return int */ diff --git a/src/pocketmine/item/Bow.php b/src/pocketmine/item/Bow.php index 39e11a4c18..3e785c71d7 100644 --- a/src/pocketmine/item/Bow.php +++ b/src/pocketmine/item/Bow.php @@ -67,12 +67,20 @@ class Bow extends Tool{ $entity = Entity::createEntity("Arrow", $player->getLevel(), $nbt, $player, $force == 2); if($entity instanceof Projectile){ $infinity = $this->hasEnchantment(Enchantment::INFINITY); - if($infinity and $entity instanceof ArrowEntity){ - $entity->setPickupMode(ArrowEntity::PICKUP_CREATIVE); + if($entity instanceof ArrowEntity){ + if($infinity){ + $entity->setPickupMode(ArrowEntity::PICKUP_CREATIVE); + } + if(($punchLevel = $this->getEnchantmentLevel(Enchantment::PUNCH)) > 0){ + $entity->setPunchKnockback($punchLevel); + } } if(($powerLevel = $this->getEnchantmentLevel(Enchantment::POWER)) > 0){ $entity->setBaseDamage($entity->getBaseDamage() + (($powerLevel + 1) / 2)); } + if($this->hasEnchantment(Enchantment::FLAME)){ + $entity->setOnFire($entity->getFireTicks() * 20 + 100); + } $ev = new EntityShootBowEvent($player, $this, $entity, $force); if($force < 0.1 or $diff < 5){ diff --git a/src/pocketmine/item/enchantment/Enchantment.php b/src/pocketmine/item/enchantment/Enchantment.php index 9d40e7758b..9b4e7f461f 100644 --- a/src/pocketmine/item/enchantment/Enchantment.php +++ b/src/pocketmine/item/enchantment/Enchantment.php @@ -114,7 +114,7 @@ class Enchantment{ self::registerEnchantment(new ProtectionEnchantment(self::PROJECTILE_PROTECTION, "%enchantment.protect.projectile", self::RARITY_UNCOMMON, self::SLOT_ARMOR, self::SLOT_NONE, 4, 1.5, [ EntityDamageEvent::CAUSE_PROJECTILE ])); - + self::registerEnchantment(new Enchantment(self::THORNS, "%enchantment.thorns", self::RARITY_MYTHIC, self::SLOT_TORSO, self::SLOT_HEAD | self::SLOT_LEGS | self::SLOT_FEET, 3)); self::registerEnchantment(new Enchantment(self::RESPIRATION, "%enchantment.oxygen", self::RARITY_RARE, self::SLOT_HEAD, self::SLOT_NONE, 3)); self::registerEnchantment(new Enchantment(self::EFFICIENCY, "%enchantment.digging", self::RARITY_COMMON, self::SLOT_DIG, self::SLOT_SHEARS, 5)); @@ -122,7 +122,8 @@ class Enchantment{ self::registerEnchantment(new Enchantment(self::UNBREAKING, "%enchantment.durability", self::RARITY_UNCOMMON, self::SLOT_DIG | self::SLOT_ARMOR | self::SLOT_FISHING_ROD | self::SLOT_BOW, self::SLOT_TOOL | self::SLOT_CARROT_STICK | self::SLOT_ELYTRA, 3)); self::registerEnchantment(new Enchantment(self::POWER, "%enchantment.arrowDamage", self::RARITY_COMMON, self::SLOT_BOW, self::SLOT_NONE, 5)); - + self::registerEnchantment(new Enchantment(self::PUNCH, "%enchantment.arrowKnockback", self::RARITY_RARE, self::SLOT_BOW, self::SLOT_NONE, 2)); + self::registerEnchantment(new Enchantment(self::FLAME, "%enchantment.arrowFire", self::RARITY_RARE, self::SLOT_BOW, self::SLOT_NONE, 1)); self::registerEnchantment(new Enchantment(self::INFINITY, "%enchantment.arrowInfinite", self::RARITY_MYTHIC, self::SLOT_BOW, self::SLOT_NONE, 1)); self::registerEnchantment(new Enchantment(self::VANISHING, "%enchantment.curse.vanishing", self::RARITY_MYTHIC, self::SLOT_NONE, self::SLOT_ALL, 1));