Illager banners finally working

closes #2951
This commit is contained in:
Dylan K. Taylor 2025-08-24 15:31:10 +01:00
parent 7c521b456e
commit 8f9478e82f
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
13 changed files with 260 additions and 12 deletions

View File

@ -50,6 +50,10 @@ abstract class BaseBanner extends Transparent implements Colored{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileBanner){
if($tile->getType() === TileBanner::TYPE_OMINOUS){
//illager banner is implemented as a separate block, as it doesn't support base color or custom patterns
return $this->getOminousVersion();
}
$this->color = $tile->getBaseColor();
$this->setPatterns($tile->getPatterns());
}
@ -57,6 +61,8 @@ abstract class BaseBanner extends Transparent implements Colored{
return $this;
}
abstract protected function getOminousVersion() : Block;
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);

View File

@ -0,0 +1,85 @@
<?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\block;
use pocketmine\block\tile\Banner as TileBanner;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function assert;
abstract class BaseOminousBanner extends Transparent{
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
assert($tile instanceof TileBanner);
$tile->setBaseColor(DyeColor::WHITE);
$tile->setPatterns([]);
$tile->setType(TileBanner::TYPE_OMINOUS);
}
public function isSolid() : bool{
return false;
}
public function getMaxStackSize() : int{
return 16;
}
public function getFuelTime() : int{
return 300;
}
protected function recalculateCollisionBoxes() : array{
return [];
}
public function getSupportType(int $facing) : SupportType{
return SupportType::NONE;
}
private function canBeSupportedBy(Block $block) : bool{
return $block->isSolid();
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
abstract protected function getSupportingFace() : int;
public function onNearbyBlockChange() : void{
if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){
$this->position->getWorld()->useBreakOn($this->position);
}
}
}

View File

@ -787,8 +787,10 @@ final class BlockTypeIds{
public const RESIN_CLUMP = 10757;
public const CHISELED_RESIN_BRICKS = 10758;
public const RESPAWN_ANCHOR = 10759;
public const OMINOUS_BANNER = 10760;
public const OMINOUS_WALL_BANNER = 10761;
public const FIRST_UNUSED_BLOCK_ID = 10760;
public const FIRST_UNUSED_BLOCK_ID = 10762;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

View File

@ -34,6 +34,10 @@ use pocketmine\world\BlockTransaction;
final class FloorBanner extends BaseBanner implements SignLikeRotation{
use SignLikeRotationTrait;
protected function getOminousVersion() : Block{
return VanillaBlocks::OMINOUS_BANNER()->setRotation($this->rotation);
}
protected function getSupportingFace() : int{
return Facing::DOWN;
}

View File

@ -0,0 +1,53 @@
<?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\block;
use pocketmine\block\utils\SignLikeRotation;
use pocketmine\block\utils\SignLikeRotationTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class OminousFloorBanner extends BaseOminousBanner implements SignLikeRotation{
use SignLikeRotationTrait;
//TODO: duplicated code :(
protected function getSupportingFace() : int{
return Facing::DOWN;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face !== Facing::UP){
return false;
}
if($player !== null){
$this->rotation = self::getRotationFromYaw($player->getLocation()->getYaw());
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}

View File

@ -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\block;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\item\Item;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class OminousWallBanner extends BaseOminousBanner implements HorizontalFacing{
use HorizontalFacingTrait;
protected function getSupportingFace() : int{
return Facing::opposite($this->facing);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(Facing::axis($face) === Axis::Y){
return false;
}
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
}

View File

@ -587,6 +587,8 @@ use function strtolower;
* @method static WallSign OAK_WALL_SIGN()
* @method static Wood OAK_WOOD()
* @method static Opaque OBSIDIAN()
* @method static OminousFloorBanner OMINOUS_BANNER()
* @method static OminousWallBanner OMINOUS_WALL_BANNER()
* @method static Flower ORANGE_TULIP()
* @method static Flower OXEYE_DAISY()
* @method static PackedIce PACKED_ICE()
@ -873,6 +875,8 @@ final class VanillaBlocks{
$bannerBreakInfo = new Info(BreakInfo::axe(1.0));
self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class);
self::register("wall_banner", fn(BID $id) => new WallBanner($id, "Wall Banner", $bannerBreakInfo), TileBanner::class);
self::register("ominous_banner", fn(BID $id) => new OminousFloorBanner($id, "Ominous Banner", $bannerBreakInfo), TileBanner::class);
self::register("ominous_wall_banner", fn(BID $id) => new OminousWallBanner($id, "Ominous Wall Banner", $bannerBreakInfo), TileBanner::class);
self::register("barrel", fn(BID $id) => new Barrel($id, "Barrel", new Info(BreakInfo::axe(2.5))), TileBarrel::class);
self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible())));
self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class);

View File

@ -35,6 +35,10 @@ use pocketmine\world\BlockTransaction;
final class WallBanner extends BaseBanner implements HorizontalFacing{
use HorizontalFacingTrait;
protected function getOminousVersion() : Block{
return VanillaBlocks::OMINOUS_WALL_BANNER()->setFacing($this->facing);
}
protected function getSupportingFace() : int{
return Facing::opposite($this->facing);
}

View File

@ -41,6 +41,10 @@ class Banner extends Spawnable{
public const TAG_PATTERNS = "Patterns";
public const TAG_PATTERN_COLOR = "Color";
public const TAG_PATTERN_NAME = "Pattern";
public const TAG_TYPE = "Type";
public const TYPE_NORMAL = 0;
public const TYPE_OMINOUS = 1;
private DyeColor $baseColor = DyeColor::BLACK;
@ -50,6 +54,8 @@ class Banner extends Spawnable{
*/
private array $patterns = [];
private int $type = self::TYPE_NORMAL;
public function readSaveData(CompoundTag $nbt) : void{
$colorIdMap = DyeColorIdMap::getInstance();
if(
@ -75,6 +81,8 @@ class Banner extends Spawnable{
$this->patterns[] = new BannerPatternLayer($patternType, $patternColor);
}
}
$this->type = $nbt->getInt(self::TAG_TYPE);
}
protected function writeSaveData(CompoundTag $nbt) : void{
@ -89,6 +97,7 @@ class Banner extends Spawnable{
);
}
$nbt->setTag(self::TAG_PATTERNS, $patterns);
$nbt->setInt(self::TAG_TYPE, $this->type);
}
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
@ -103,6 +112,7 @@ class Banner extends Spawnable{
);
}
$nbt->setTag(self::TAG_PATTERNS, $patterns);
$nbt->setInt(self::TAG_TYPE, $this->type);
}
/**
@ -136,6 +146,10 @@ class Banner extends Spawnable{
$this->patterns = $patterns;
}
public function getType() : int{ return $this->type; }
public function setType(int $type) : void{ $this->type = $type; }
public function getDefaultName() : string{
return "Banner";
}

View File

@ -55,6 +55,7 @@ use pocketmine\block\Farmland;
use pocketmine\block\FillableCauldron;
use pocketmine\block\Fire;
use pocketmine\block\FloorCoralFan;
use pocketmine\block\OminousFloorBanner;
use pocketmine\block\Froglight;
use pocketmine\block\FrostedIce;
use pocketmine\block\GlazedTerracotta;
@ -103,6 +104,7 @@ use pocketmine\block\utils\MushroomBlockType;
use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\block\Vine;
use pocketmine\block\OminousWallBanner;
use pocketmine\data\bedrock\block\BlockLegacyMetadata;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateNames as StateNames;
@ -154,7 +156,7 @@ final class VanillaBlockMappings{
self::registerChemistryMappings($reg, $commonProperties);
self::register1to1CustomMappings($reg, $commonProperties);
self::registerSplitMappings($reg);
self::registerSplitMappings($reg, $commonProperties);
}
private static function registerSimpleIdOnlyMappings(BlockSerializerDeserializerRegistrar $reg) : void{
@ -1476,7 +1478,7 @@ final class VanillaBlockMappings{
* These currently can't be registered in a unified way, and due to their small number it may not be worth the
* effort to implement a unified way to deal with them
*/
private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg) : void{
private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{
//big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state
$reg->serializer->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{
return Writer::create(Ids::BIG_DRIPLEAF)
@ -1557,5 +1559,18 @@ final class VanillaBlockMappings{
->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE))
->setTop($top);
});
//these only exist within PM (mapped from tile properties) as they don't support the same properties as a
//normal banner
$reg->serializer->map(Blocks::OMINOUS_BANNER(), function(OminousFloorBanner $block) use ($commonProperties) : Writer{
$writer = Writer::create(Ids::STANDING_BANNER);
$commonProperties->floorSignLikeRotation->serialize($block, $writer);
return $writer;
});
$reg->serializer->map(Blocks::OMINOUS_WALL_BANNER(), function(OminousWallBanner $block) use ($commonProperties) : Writer{
$writer = Writer::create(Ids::WALL_BANNER);
$commonProperties->horizontalFacingClassic->serialize($block, $writer);
return $writer;
});
}
}

View File

@ -26,6 +26,7 @@ namespace pocketmine\data\bedrock\item;
use pocketmine\block\Bed;
use pocketmine\block\Block;
use pocketmine\block\CopperDoor;
use pocketmine\block\tile\Banner as TileBanner;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\VanillaBlocks as Blocks;
@ -46,6 +47,7 @@ use pocketmine\item\Potion;
use pocketmine\item\SplashPotion;
use pocketmine\item\SuspiciousStew;
use pocketmine\item\VanillaItems as Items;
use pocketmine\nbt\tag\CompoundTag;
final class ItemSerializerDeserializerRegistrar{
@ -487,14 +489,6 @@ final class ItemSerializerDeserializerRegistrar{
* in a unified manner.
*/
private function register1to1ItemWithMetaMappings() : void{
$this->map1to1ItemWithMeta(
Ids::BANNER,
Items::BANNER(),
function(Banner $item, int $meta) : void{
$item->setColor(DyeColorIdMap::getInstance()->fromInvertedId($meta) ?? throw new ItemTypeDeserializeException("Unknown banner meta $meta"));
},
fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor())
);
$this->map1to1ItemWithMeta(
Ids::GOAT_HORN,
Items::GOAT_HORN(),
@ -550,6 +544,21 @@ final class ItemSerializerDeserializerRegistrar{
$this->deserializer?->map($id, fn() => Items::DYE()->setColor($color));
}
$this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor())));
$this->deserializer?->map(Ids::BANNER, function(Data $data) : Item{
$type = $data->getTag()?->getInt(TileBanner::TAG_TYPE) ?? TileBanner::TYPE_NORMAL;
if($type === TileBanner::TYPE_OMINOUS){
return Items::OMINOUS_BANNER();
}
$color = DyeColorIdMap::getInstance()->fromInvertedId($data->getMeta()) ?? throw new ItemTypeDeserializeException("Unknown banner meta " . $data->getMeta());
return Items::BANNER()->setColor($color);
});
$this->serializer?->map(Items::OMINOUS_BANNER(), fn() => new Data(Ids::BANNER, tag: CompoundTag::create()
->setInt(TileBanner::TAG_TYPE, TileBanner::TYPE_OMINOUS))
);
$this->serializer?->map(Items::BANNER(), function(Banner $item) : Data{
return new Data(Ids::BANNER, DyeColorIdMap::getInstance()->toInvertedId($item->getColor()));
});
}
/**

View File

@ -334,8 +334,9 @@ final class ItemTypeIds{
public const RECORD_CREATOR = 20295;
public const RECORD_CREATOR_MUSIC_BOX = 20296;
public const RECORD_PRECIPICE = 20297;
public const OMINOUS_BANNER = 20298;
public const FIRST_UNUSED_ITEM_ID = 20298;
public const FIRST_UNUSED_ITEM_ID = 20299;
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

View File

@ -242,6 +242,7 @@ use function strtolower;
* @method static Item NETHER_STAR()
* @method static Boat OAK_BOAT()
* @method static ItemBlockWallOrFloor OAK_SIGN()
* @method static ItemBlockWallOrFloor OMINOUS_BANNER()
* @method static PaintingItem PAINTING()
* @method static ItemBlockWallOrFloor PALE_OAK_SIGN()
* @method static Item PAPER()
@ -540,6 +541,7 @@ final class VanillaItems{
public function isFireProof() : bool{ return true; }
});
self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN()));
self::register("ominous_banner", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OMINOUS_BANNER(), Blocks::OMINOUS_WALL_BANNER()));
self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting"));
self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN()));
self::register("paper", fn(IID $id) => new Item($id, "Paper"));