Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions generated/item/VanillaItems.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ final class VanillaItems{
private static BannerPattern $_mCREEPER_BANNER_PATTERN;
private static HangingSign $_mCRIMSON_HANGING_SIGN;
private static ItemBlockWallOrFloor $_mCRIMSON_SIGN;
private static Crossbow $_mCROSSBOW;
private static Cushion $_mCUSHION;
private static Boat $_mDARK_OAK_BOAT;
private static HangingSign $_mDARK_OAK_HANGING_SIGN;
Expand Down Expand Up @@ -549,6 +550,7 @@ private static function getInitAssigners() : array{
"creeper_banner_pattern" => fn(BannerPattern $v) => self::$_mCREEPER_BANNER_PATTERN = $v,
"crimson_hanging_sign" => fn(HangingSign $v) => self::$_mCRIMSON_HANGING_SIGN = $v,
"crimson_sign" => fn(ItemBlockWallOrFloor $v) => self::$_mCRIMSON_SIGN = $v,
"crossbow" => fn(Crossbow $v) => self::$_mCROSSBOW = $v,
"cushion" => fn(Cushion $v) => self::$_mCUSHION = $v,
"dark_oak_boat" => fn(Boat $v) => self::$_mDARK_OAK_BOAT = $v,
"dark_oak_hanging_sign" => fn(HangingSign $v) => self::$_mDARK_OAK_HANGING_SIGN = $v,
Expand Down Expand Up @@ -1401,6 +1403,11 @@ public static function CRIMSON_SIGN() : ItemBlockWallOrFloor{
return clone self::$_mCRIMSON_SIGN;
}

public static function CROSSBOW() : Crossbow{
if(!isset(self::$_mCROSSBOW)){ self::init(); }
return clone self::$_mCROSSBOW;
}

public static function CUSHION() : Cushion{
if(!isset(self::$_mCUSHION)){ self::init(); }
return clone self::$_mCUSHION;
Expand Down
21 changes: 21 additions & 0 deletions generated/item/enchantment/VanillaEnchantments.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,13 @@ final class VanillaEnchantments{
private static KnockbackEnchantment $_mKNOCKBACK;
private static Enchantment $_mLUNGE;
private static Enchantment $_mMENDING;
private static Enchantment $_mMULTISHOT;
private static Enchantment $_mPIERCING;
private static Enchantment $_mPOWER;
private static ProtectionEnchantment $_mPROJECTILE_PROTECTION;
private static ProtectionEnchantment $_mPROTECTION;
private static Enchantment $_mPUNCH;
private static Enchantment $_mQUICK_CHARGE;
private static Enchantment $_mRESPIRATION;
private static SharpnessEnchantment $_mSHARPNESS;
private static Enchantment $_mSILK_TOUCH;
Expand Down Expand Up @@ -114,10 +117,13 @@ private static function getInitAssigners() : array{
"KNOCKBACK" => fn(KnockbackEnchantment $v) => self::$_mKNOCKBACK = $v,
"LUNGE" => fn(Enchantment $v) => self::$_mLUNGE = $v,
"MENDING" => fn(Enchantment $v) => self::$_mMENDING = $v,
"MULTISHOT" => fn(Enchantment $v) => self::$_mMULTISHOT = $v,
"PIERCING" => fn(Enchantment $v) => self::$_mPIERCING = $v,
"POWER" => fn(Enchantment $v) => self::$_mPOWER = $v,
"PROJECTILE_PROTECTION" => fn(ProtectionEnchantment $v) => self::$_mPROJECTILE_PROTECTION = $v,
"PROTECTION" => fn(ProtectionEnchantment $v) => self::$_mPROTECTION = $v,
"PUNCH" => fn(Enchantment $v) => self::$_mPUNCH = $v,
"QUICK_CHARGE" => fn(Enchantment $v) => self::$_mQUICK_CHARGE = $v,
"RESPIRATION" => fn(Enchantment $v) => self::$_mRESPIRATION = $v,
"SHARPNESS" => fn(SharpnessEnchantment $v) => self::$_mSHARPNESS = $v,
"SILK_TOUCH" => fn(Enchantment $v) => self::$_mSILK_TOUCH = $v,
Expand Down Expand Up @@ -239,6 +245,16 @@ public static function MENDING() : Enchantment{
return self::$_mMENDING;
}

public static function MULTISHOT() : Enchantment{
if(!isset(self::$_mMULTISHOT)){ self::init(); }
return self::$_mMULTISHOT;
}

public static function PIERCING() : Enchantment{
if(!isset(self::$_mPIERCING)){ self::init(); }
return self::$_mPIERCING;
}

public static function POWER() : Enchantment{
if(!isset(self::$_mPOWER)){ self::init(); }
return self::$_mPOWER;
Expand All @@ -259,6 +275,11 @@ public static function PUNCH() : Enchantment{
return self::$_mPUNCH;
}

public static function QUICK_CHARGE() : Enchantment{
if(!isset(self::$_mQUICK_CHARGE)){ self::init(); }
return self::$_mQUICK_CHARGE;
}

public static function RESPIRATION() : Enchantment{
if(!isset(self::$_mRESPIRATION)){ self::init(); }
return self::$_mRESPIRATION;
Expand Down
4 changes: 4 additions & 0 deletions src/data/bedrock/EnchantmentIdMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ private function __construct(){

$this->register(EnchantmentIds::VANISHING, VanillaEnchantments::VANISHING());

$this->register(EnchantmentIds::MULTISHOT, VanillaEnchantments::MULTISHOT());
$this->register(EnchantmentIds::PIERCING, VanillaEnchantments::PIERCING());
$this->register(EnchantmentIds::QUICK_CHARGE, VanillaEnchantments::QUICK_CHARGE());

$this->register(EnchantmentIds::SWIFT_SNEAK, VanillaEnchantments::SWIFT_SNEAK());

$this->register(EnchantmentIds::FROST_WALKER, VanillaEnchantments::FROST_WALKER());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ private function register1to1ItemMappings() : void{
$this->map1to1Item(Ids::CRIMSON_HANGING_SIGN, Items::CRIMSON_HANGING_SIGN());
$this->map1to1Item(Ids::CRIMSON_SIGN, Items::CRIMSON_SIGN());
$this->map1to1Item(Ids::CHERRY_BOAT, Items::CHERRY_BOAT());
$this->map1to1Item(Ids::CROSSBOW, Items::CROSSBOW());
$this->map1to1Item(Ids::DARK_OAK_BOAT, Items::DARK_OAK_BOAT());
$this->map1to1Item(Ids::DARK_OAK_HANGING_SIGN, Items::DARK_OAK_HANGING_SIGN());
$this->map1to1Item(Ids::DARK_OAK_SIGN, Items::DARK_OAK_SIGN());
Expand Down
84 changes: 84 additions & 0 deletions src/entity/object/CrossbowFireworkRocket.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?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.
*
* Original work by the PocketMine Team.
* https://www.pocketmine.net/
*
* @author Altay Team
* @link https://github.com/altayofficial
*/

declare(strict_types=1);

namespace pocketmine\entity\object;

use pocketmine\entity\Living;
use pocketmine\math\Vector3;

class CrossbowFireworkRocket extends FireworkRocket{

/**
* Ticks during which the rocket cannot hit the entity which shot it, so that it doesn't explode in its face.
*/
private const OWNER_IMMUNITY_TICKS = 2;

private ?Vector3 $lastMoveStart = null;

protected function tickFlight() : void{
//unlike a rocket launched from the ground, this one flies straight towards where the crossbow was aimed at
}

protected function move(float $dx, float $dy, float $dz) : void{
$this->lastMoveStart = $this->location->asVector3();
parent::move($dx, $dy, $dz);
}

protected function entityBaseTick(int $tickDiff = 1) : bool{
$hasUpdate = parent::entityBaseTick($tickDiff);

if(!$this->isFlaggedForDespawn() && $this->hitEntity() !== null){
$this->flagForDespawn();
$this->explode();
}

return $hasUpdate;
}

private function hitEntity() : ?Living{
$owningEntityId = $this->getOwningEntityId();
$end = $this->location->asVector3();
$start = $this->lastMoveStart ?? $end;
$delta = $end->subtractVector($start);
$halfWidth = $this->getSize()->getWidth() / 2;

foreach($this->getWorld()->getNearbyEntities($this->boundingBox->addCoord(-$delta->x, -$delta->y, -$delta->z), $this) as $entity){
if(!$entity instanceof Living || !$entity->canBeCollidedWith()){
continue;
}

if($entity->getId() === $owningEntityId && $this->ticksLived <= self::OWNER_IMMUNITY_TICKS){
continue;
}

$entityBB = $entity->getBoundingBox()->expandedCopy($halfWidth, $halfWidth, $halfWidth);
if($entityBB->isVectorInside($end) || $entityBB->calculateIntercept($start, $end) !== null){
return $entity;
}
}

return null;
}
}
59 changes: 59 additions & 0 deletions src/entity/projectile/Arrow.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
use pocketmine\player\Player;
use pocketmine\world\sound\ArrowHitSound;
use function ceil;
use function count;
use function mt_rand;
use function sqrt;

Expand All @@ -54,6 +55,11 @@ public static function getNetworkTypeId() : string{ return EntityIds::ARROW; }
public const PICKUP_ANY = 1;
public const PICKUP_CREATIVE = 2;

/**
* Speed kept by a piercing arrow after going through an entity.
*/
private const PIERCING_SPEED_MULTIPLIER = 0.99;

private const TAG_PICKUP = "pickup"; //TAG_Byte
public const TAG_CRIT = "crit"; //TAG_Byte
private const TAG_LIFE = "life"; //TAG_Short
Expand All @@ -63,6 +69,16 @@ public static function getNetworkTypeId() : string{ return EntityIds::ARROW; }
protected float $punchKnockback = 0.0;
protected int $collideTicks = 0;
protected bool $critical = false;
protected int $piercing = 0;
private bool $piercedThisMove = false;

/**
* Entities the arrow already went through, so that it doesn't damage them again on the next tick.
*
* @var true[]
* @phpstan-var array<int, true>
*/
protected array $piercedEntities = [];

public function __construct(Location $location, ?Entity $shootingEntity, bool $critical, ?CompoundTag $nbt = null){
parent::__construct($location, $shootingEntity, $nbt);
Expand Down Expand Up @@ -147,8 +163,51 @@ protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void
$this->broadcastAnimation(new ArrowShakeAnimation($this, 7));
}

/**
* Returns how many entities the arrow can go through before stopping.
*/
public function getPiercing() : int{
return $this->piercing;
}

public function setPiercing(int $piercing) : void{
if($piercing < 0){
throw new \InvalidArgumentException("Piercing must not be negative");
}
$this->piercing = $piercing;
}

public function canCollideWith(Entity $entity) : bool{
return !isset($this->piercedEntities[$entity->getId()]) && parent::canCollideWith($entity);
}

protected function despawnsOnEntityHit() : bool{
return count($this->piercedEntities) > $this->piercing;
}

/**
* A piercing arrow keeps flying after going through an entity, so it must not be left in the "stuck" state that
* {@link Projectile::move()} puts it in after a hit, otherwise it ignores every entity during its next step.
*/
protected function move(float $dx, float $dy, float $dz) : void{
parent::move($dx, $dy, $dz);
if($this->piercedThisMove){
$this->isCollided = $this->onGround = false;
$this->piercedThisMove = false;
}
}

protected function onHitEntity(Entity $entityHit, RayTraceResult $hitResult) : void{
$this->piercedEntities[$entityHit->getId()] = true;

parent::onHitEntity($entityHit, $hitResult);

if(!$this->isFlaggedForDespawn()){
//without this the projectile code would zero the motion, stopping the arrow inside the entity it just went through
$this->motion = $this->motion->multiply(self::PIERCING_SPEED_MULTIPLIER);
Comment thread
NaySurGithub marked this conversation as resolved.
$this->piercedThisMove = true;
}

if($this->punchKnockback > 0){
$horizontalSpeed = sqrt($this->motion->x ** 2 + $this->motion->z ** 2);
if($horizontalSpeed > 0){
Expand Down
Loading
Loading