From e77f2c5198ebe58b06f3f857674b0c8588c7f250 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Sat, 16 Nov 2024 20:57:57 +0300 Subject: [PATCH] Implemented End Crystal (#4715) Co-authored-by: Dylan T. --- .../ItemSerializerDeserializerRegistrar.php | 1 + src/entity/EntityFactory.php | 5 + src/entity/object/EndCrystal.php | 140 ++++++++++++++++++ src/entity/projectile/Projectile.php | 3 +- src/item/EndCrystal.php | 59 ++++++++ src/item/ItemTypeIds.php | 3 +- src/item/StringToItemParser.php | 1 + src/item/VanillaItems.php | 2 + 8 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/entity/object/EndCrystal.php create mode 100644 src/item/EndCrystal.php diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 661a4ed7d..7803cea5c 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -232,6 +232,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::EMERALD, Items::EMERALD()); $this->map1to1Item(Ids::ENCHANTED_BOOK, Items::ENCHANTED_BOOK()); $this->map1to1Item(Ids::ENCHANTED_GOLDEN_APPLE, Items::ENCHANTED_GOLDEN_APPLE()); + $this->map1to1Item(Ids::END_CRYSTAL, Items::END_CRYSTAL()); $this->map1to1Item(Ids::ENDER_PEARL, Items::ENDER_PEARL()); $this->map1to1Item(Ids::EXPERIENCE_BOTTLE, Items::EXPERIENCE_BOTTLE()); $this->map1to1Item(Ids::EYE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index d8d189cff..3d53233ab 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -32,6 +32,7 @@ use pocketmine\data\bedrock\PotionTypeIdMap; use pocketmine\data\bedrock\PotionTypeIds; use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\EntityDataHelper as Helper; +use pocketmine\entity\object\EndCrystal; use pocketmine\entity\object\ExperienceOrb; use pocketmine\entity\object\FallingBlock; use pocketmine\entity\object\ItemEntity; @@ -92,6 +93,10 @@ final class EntityFactory{ return new Egg(Helper::parseLocation($nbt, $world), null, $nbt); }, ['Egg', 'minecraft:egg']); + $this->register(EndCrystal::class, function(World $world, CompoundTag $nbt) : EndCrystal{ + return new EndCrystal(Helper::parseLocation($nbt, $world), $nbt); + }, ['EnderCrystal', 'minecraft:ender_crystal']); + $this->register(EnderPearl::class, function(World $world, CompoundTag $nbt) : EnderPearl{ return new EnderPearl(Helper::parseLocation($nbt, $world), null, $nbt); }, ['ThrownEnderpearl', 'minecraft:ender_pearl']); diff --git a/src/entity/object/EndCrystal.php b/src/entity/object/EndCrystal.php new file mode 100644 index 000000000..2c4a4f7e5 --- /dev/null +++ b/src/entity/object/EndCrystal.php @@ -0,0 +1,140 @@ +showBase; + } + + public function setShowBase(bool $showBase) : void{ + $this->showBase = $showBase; + $this->networkPropertiesDirty = true; + } + + public function getBeamTarget() : ?Vector3{ + return $this->beamTarget; + } + + public function setBeamTarget(?Vector3 $beamTarget) : void{ + $this->beamTarget = $beamTarget; + $this->networkPropertiesDirty = true; + } + + public function attack(EntityDamageEvent $source) : void{ + parent::attack($source); + if( + $source->getCause() !== EntityDamageEvent::CAUSE_VOID && + !$this->isFlaggedForDespawn() && + !$source->isCancelled() + ){ + $this->flagForDespawn(); + $this->explode(); + } + } + + protected function initEntity(CompoundTag $nbt) : void{ + parent::initEntity($nbt); + + $this->setMaxHealth(1); + $this->setHealth(1); + + $this->setShowBase($nbt->getByte(self::TAG_SHOWBASE, 0) === 1); + + if( + ($beamXTag = $nbt->getTag(self::TAG_BLOCKTARGET_X)) instanceof IntTag && + ($beamYTag = $nbt->getTag(self::TAG_BLOCKTARGET_Y)) instanceof IntTag && + ($beamZTag = $nbt->getTag(self::TAG_BLOCKTARGET_Z)) instanceof IntTag + ){ + $this->setBeamTarget(new Vector3($beamXTag->getValue(), $beamYTag->getValue(), $beamZTag->getValue())); + } + } + + public function saveNBT() : CompoundTag{ + $nbt = parent::saveNBT(); + + $nbt->setByte(self::TAG_SHOWBASE, $this->showBase ? 1 : 0); + if($this->beamTarget !== null){ + $nbt->setInt(self::TAG_BLOCKTARGET_X, $this->beamTarget->getFloorX()); + $nbt->setInt(self::TAG_BLOCKTARGET_Y, $this->beamTarget->getFloorY()); + $nbt->setInt(self::TAG_BLOCKTARGET_Z, $this->beamTarget->getFloorZ()); + } + return $nbt; + } + + public function explode() : void{ + $ev = new EntityPreExplodeEvent($this, 6); + $ev->call(); + if(!$ev->isCancelled()){ + $explosion = new Explosion($this->getPosition(), $ev->getRadius(), $this); + if($ev->isBlockBreaking()){ + $explosion->explodeA(); + } + $explosion->explodeB(); + } + } + + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ + parent::syncNetworkData($properties); + + $properties->setGenericFlag(EntityMetadataFlags::SHOWBASE, $this->showBase); + $properties->setBlockPos(EntityMetadataProperties::BLOCK_TARGET, BlockPosition::fromVector3($this->beamTarget ?? Vector3::zero())); + } +} diff --git a/src/entity/projectile/Projectile.php b/src/entity/projectile/Projectile.php index 55950b6a6..0abc274b5 100644 --- a/src/entity/projectile/Projectile.php +++ b/src/entity/projectile/Projectile.php @@ -28,6 +28,7 @@ use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\Entity; use pocketmine\entity\Living; use pocketmine\entity\Location; +use pocketmine\entity\object\EndCrystal; use pocketmine\event\entity\EntityCombustByEntityEvent; use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; @@ -96,7 +97,7 @@ abstract class Projectile extends Entity{ } public function canCollideWith(Entity $entity) : bool{ - return $entity instanceof Living && !$this->onGround; + return ($entity instanceof Living || $entity instanceof EndCrystal) && !$this->onGround; } public function canBeCollidedWith() : bool{ diff --git a/src/item/EndCrystal.php b/src/item/EndCrystal.php new file mode 100644 index 000000000..320d657e6 --- /dev/null +++ b/src/item/EndCrystal.php @@ -0,0 +1,59 @@ +getTypeId() === BlockTypeIds::OBSIDIAN || $blockClicked->getTypeId() === BlockTypeIds::BEDROCK){ + $pos = $blockClicked->getPosition(); + $world = $pos->getWorld(); + $bb = AxisAlignedBB::one() + ->offset($pos->getX(), $pos->getY(), $pos->getZ()) + ->extend(Facing::UP, 1); + if( + count($world->getNearbyEntities($bb)) === 0 && + $blockClicked->getSide(Facing::UP)->getTypeId() === BlockTypeIds::AIR && + $blockClicked->getSide(Facing::UP, 2)->getTypeId() === BlockTypeIds::AIR + ){ + $crystal = new EntityEndCrystal(Location::fromObject($pos->add(0.5, 1, 0.5), $world)); + $crystal->spawnToAll(); + + $this->pop(); + return ItemUseResult::SUCCESS; + } + } + return ItemUseResult::NONE; + } +} diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index 60bae0dd3..96ba2a867 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -325,8 +325,9 @@ final class ItemTypeIds{ public const PITCHER_POD = 20286; public const NAME_TAG = 20287; public const GOAT_HORN = 20288; + public const END_CRYSTAL = 20289; - public const FIRST_UNUSED_ITEM_ID = 20289; + public const FIRST_UNUSED_ITEM_ID = 20290; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index e03db28bc..4dae231ac 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1330,6 +1330,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("enchanted_book", fn() => Items::ENCHANTED_BOOK()); $result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE()); $result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE()); + $result->register("end_crystal", fn() => Items::END_CRYSTAL()); $result->register("ender_pearl", fn() => Items::ENDER_PEARL()); $result->register("experience_bottle", fn() => Items::EXPERIENCE_BOTTLE()); $result->register("eye_armor_trim_smithing_template", fn() => Items::EYE_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index a3366b85e..5899b6357 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -158,6 +158,7 @@ use function strtolower; * @method static EnchantedBook ENCHANTED_BOOK() * @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE() * @method static EnderPearl ENDER_PEARL() + * @method static EndCrystal END_CRYSTAL() * @method static ExperienceBottle EXPERIENCE_BOTTLE() * @method static Item EYE_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static Item FEATHER() @@ -479,6 +480,7 @@ final class VanillaItems{ self::register("emerald", fn(IID $id) => new Item($id, "Emerald")); self::register("enchanted_book", fn(IID $id) => new EnchantedBook($id, "Enchanted Book", [EnchantmentTags::ALL])); self::register("enchanted_golden_apple", fn(IID $id) => new GoldenAppleEnchanted($id, "Enchanted Golden Apple")); + self::register("end_crystal", fn(IID $id) => new EndCrystal($id, "End Crystal")); self::register("ender_pearl", fn(IID $id) => new EnderPearl($id, "Ender Pearl")); self::register("experience_bottle", fn(IID $id) => new ExperienceBottle($id, "Bottle o' Enchanting")); self::register("feather", fn(IID $id) => new Item($id, "Feather"));