Implemented coral and coral fans

this implementation is very rough due to having to hack around lots more MCPE bullshit, and currently doesn't allow dynamic coral types; but it's there. We'll clean this up after 1.13 migration is done.
This commit is contained in:
Dylan K. Taylor 2021-04-17 02:04:10 +01:00
parent d5e1b4bd39
commit a32eb4ebc3
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
9 changed files with 389 additions and 7 deletions

79
src/block/BaseCoral.php Normal file
View File

@ -0,0 +1,79 @@
<?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\CoralType;
use pocketmine\item\Item;
abstract class BaseCoral extends Transparent{
protected CoralType $coralType;
protected bool $dead = false;
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo, CoralType $coralType){
parent::__construct($idInfo, $name, $breakInfo);
$this->coralType = $coralType;
}
public function getCoralType() : CoralType{ return $this->coralType; }
public function isDead() : bool{ return $this->dead; }
/** @return $this */
public function setDead(bool $dead) : self{
$this->dead = $dead;
return $this;
}
public function onNearbyBlockChange() : void{
if(!$this->dead){
$world = $this->pos->getWorld();
$hasWater = false;
foreach($this->pos->sides() as $vector3){
if($world->getBlock($vector3) instanceof Water){
$hasWater = true;
break;
}
}
//TODO: check water inside the block itself (not supported on the API yet)
if(!$hasWater){
$world->setBlock($this->pos, $this->setDead(true));
}
}
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
public function isAffectedBySilkTouch() : bool{
return true;
}
public function isSolid() : bool{ return false; }
protected function recalculateCollisionBoxes() : array{ return []; }
}

View File

@ -45,9 +45,11 @@ use pocketmine\block\tile\Jukebox as TileJukebox;
use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner;
use pocketmine\block\tile\Note as TileNote;
use pocketmine\block\tile\Skull as TileSkull;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\InvalidBlockStateException;
use pocketmine\block\utils\TreeType;
use pocketmine\data\bedrock\CoralTypeIdMap;
use pocketmine\data\bedrock\DyeColorIdMap;
use pocketmine\item\Item;
use pocketmine\item\ItemIds;
@ -479,6 +481,29 @@ class BlockFactory{
$this->registerMushroomBlocks();
foreach(CoralType::getAll() as $coralType){
$coralTypeId = CoralTypeIdMap::getInstance()->toId($coralType);
$coralTypeName = $coralType->getDisplayName();
$this->register(new Coral(
new BID(Ids::CORAL, $coralTypeId),
"$coralTypeName Coral",
BlockBreakInfo::instant(),
$coralType
));
$this->register(new FloorCoralFan(
new BlockIdentifierFlattened(Ids::CORAL_FAN, Ids::CORAL_FAN_DEAD, $coralTypeId, ItemIds::CORAL_FAN),
"$coralTypeName Coral Fan",
BlockBreakInfo::instant(),
$coralType
));
$this->register(new WallCoralFan(
BlockLegacyIdHelper::getWallCoralFanIdentifier($coralType),
"$coralTypeName Wall Coral Fan",
BlockBreakInfo::instant(),
$coralType
));
}
//region --- auto-generated TODOs for bedrock-1.11.0 ---
//TODO: minecraft:bell
//TODO: minecraft:blast_furnace
@ -492,12 +517,6 @@ class BlockFactory{
//TODO: minecraft:command_block
//TODO: minecraft:composter
//TODO: minecraft:conduit
//TODO: minecraft:coral
//TODO: minecraft:coral_fan
//TODO: minecraft:coral_fan_dead
//TODO: minecraft:coral_fan_hang
//TODO: minecraft:coral_fan_hang2
//TODO: minecraft:coral_fan_hang3
//TODO: minecraft:dispenser
//TODO: minecraft:dropper
//TODO: minecraft:end_gateway

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\BlockIdentifier as BID;
use pocketmine\block\BlockLegacyIds as Ids;
use pocketmine\block\tile\Sign as TileSign;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\TreeType;
use pocketmine\item\ItemIds;
@ -227,4 +228,15 @@ final class BlockLegacyIdHelper{
}
return new BlockIdentifierFlattened($id[0], $id[1], $meta);
}
public static function getWallCoralFanIdentifier(CoralType $type) : BlockIdentifier{
switch($type->id()){
case CoralType::TUBE()->id(): return new BID(Ids::CORAL_FAN_HANG, 0, ItemIds::CORAL_FAN);
case CoralType::BRAIN()->id(): return new BID(Ids::CORAL_FAN_HANG, 1, ItemIds::CORAL_FAN);
case CoralType::BUBBLE()->id(): return new BID(Ids::CORAL_FAN_HANG2, 0, ItemIds::CORAL_FAN);
case CoralType::FIRE()->id(): return new BID(Ids::CORAL_FAN_HANG2, 1, ItemIds::CORAL_FAN);
case CoralType::HORN()->id(): return new BID(Ids::CORAL_FAN_HANG3, 0, ItemIds::CORAL_FAN);
}
throw new AssumptionFailedError("Switch should cover all coral types");
}
}

48
src/block/Coral.php Normal file
View File

@ -0,0 +1,48 @@
<?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\item\Item;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class Coral extends BaseCoral{
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$tx->fetchBlock($blockReplace->getPos()->down())->isSolid()){
return false;
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$world = $this->pos->getWorld();
if(!$world->getBlock($this->pos->down())->isSolid()){
$world->useBreakOn($this->pos);
}else{
parent::onNearbyBlockChange();
}
}
}

111
src/block/FloorCoralFan.php Normal file
View File

@ -0,0 +1,111 @@
<?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\CoralType;
use pocketmine\data\bedrock\CoralTypeIdMap;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\ItemIds;
use pocketmine\math\Axis;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use function atan2;
use function rad2deg;
final class FloorCoralFan extends BaseCoral{
/** @var BlockIdentifierFlattened */
protected $idInfo;
private int $axis = Axis::X;
public function __construct(BlockIdentifierFlattened $idInfo, string $name, BlockBreakInfo $breakInfo, CoralType $coralType){
parent::__construct($idInfo, $name, $breakInfo, $coralType);
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->dead = $id === $this->idInfo->getSecondId();
$this->axis = ($stateMeta >> 3) === BlockLegacyMetadata::CORAL_FAN_EAST_WEST ? Axis::X : Axis::Z;
}
public function getId() : int{
return $this->dead ? $this->idInfo->getSecondId() : parent::getId();
}
public function asItem() : Item{
//TODO: HACK! workaround dead flag being lost when broken / blockpicked (original impl only uses first ID)
return ItemFactory::getInstance()->get(
$this->dead ? ItemIds::CORAL_FAN_DEAD : ItemIds::CORAL_FAN,
CoralTypeIdMap::getInstance()->toId($this->coralType)
);
}
protected function writeStateToMeta() : int{
return ($this->axis === Axis::X ? BlockLegacyMetadata::CORAL_FAN_EAST_WEST : BlockLegacyMetadata::CORAL_FAN_NORTH_SOUTH) << 3;
}
public function getStateBitmask() : int{
return 0b1000;
}
public function getAxis() : int{ return $this->axis; }
/** @return $this */
public function setAxis(int $axis) : self{
if($axis !== Axis::X && $axis !== Axis::Z){
throw new \InvalidArgumentException("Axis must be X or Z only");
}
$this->axis = $axis;
return $this;
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if(!$tx->fetchBlock($blockReplace->getPos()->down())->isSolid()){
return false;
}
if($player !== null){
$playerBlockPos = $player->getPosition()->floor();
$directionVector = $blockReplace->getPos()->subtractVector($playerBlockPos)->normalize();
$angle = rad2deg(atan2($directionVector->getZ(), $directionVector->getX()));
if($angle <= 45 || 315 <= $angle || (135 <= $angle && $angle <= 225)){
//TODO: This produces Z axis 75% of the time, because any negative angle will produce Z axis.
//This is a bug in vanilla. https://bugs.mojang.com/browse/MCPE-125311
$this->axis = Axis::Z;
}
}
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$world = $this->pos->getWorld();
if(!$world->getBlock($this->pos->down())->isSolid()){
$world->useBreakOn($this->pos);
}else{
parent::onNearbyBlockChange();
}
}
}

View File

@ -93,6 +93,9 @@ use function assert;
* @method static Torch BLUE_TORCH()
* @method static BoneBlock BONE_BLOCK()
* @method static Bookshelf BOOKSHELF()
* @method static Coral BRAIN_CORAL()
* @method static FloorCoralFan BRAIN_CORAL_FAN()
* @method static WallCoralFan BRAIN_WALL_CORAL_FAN()
* @method static BrewingStand BREWING_STAND()
* @method static Opaque BRICKS()
* @method static Slab BRICK_SLAB()
@ -104,6 +107,9 @@ use function assert;
* @method static HardenedClay BROWN_STAINED_CLAY()
* @method static Glass BROWN_STAINED_GLASS()
* @method static GlassPane BROWN_STAINED_GLASS_PANE()
* @method static Coral BUBBLE_CORAL()
* @method static FloorCoralFan BUBBLE_CORAL_FAN()
* @method static WallCoralFan BUBBLE_WALL_CORAL_FAN()
* @method static Cactus CACTUS()
* @method static Cake CAKE()
* @method static Carpet CARPET()
@ -307,6 +313,9 @@ use function assert;
* @method static Farmland FARMLAND()
* @method static TallGrass FERN()
* @method static Fire FIRE()
* @method static Coral FIRE_CORAL()
* @method static FloorCoralFan FIRE_CORAL_FAN()
* @method static WallCoralFan FIRE_WALL_CORAL_FAN()
* @method static FlowerPot FLOWER_POT()
* @method static FrostedIce FROSTED_ICE()
* @method static Furnace FURNACE()
@ -369,6 +378,9 @@ use function assert;
* @method static HardenedGlassPane HARDENED_YELLOW_STAINED_GLASS_PANE()
* @method static HayBale HAY_BALE()
* @method static Hopper HOPPER()
* @method static Coral HORN_CORAL()
* @method static FloorCoralFan HORN_CORAL_FAN()
* @method static WallCoralFan HORN_WALL_CORAL_FAN()
* @method static Ice ICE()
* @method static InfestedStone INFESTED_CHISELED_STONE_BRICK()
* @method static InfestedStone INFESTED_COBBLESTONE()
@ -603,6 +615,9 @@ use function assert;
* @method static TrappedChest TRAPPED_CHEST()
* @method static Tripwire TRIPWIRE()
* @method static TripwireHook TRIPWIRE_HOOK()
* @method static Coral TUBE_CORAL()
* @method static FloorCoralFan TUBE_CORAL_FAN()
* @method static WallCoralFan TUBE_WALL_CORAL_FAN()
* @method static UnderwaterTorch UNDERWATER_TORCH()
* @method static Vine VINES()
* @method static WallBanner WALL_BANNER()
@ -712,6 +727,9 @@ final class VanillaBlocks{
self::register("blue_torch", $factory->get(204, 5));
self::register("bone_block", $factory->get(216, 0));
self::register("bookshelf", $factory->get(47, 0));
self::register("brain_coral", $factory->get(386, 1));
self::register("brain_coral_fan", $factory->get(388, 1));
self::register("brain_wall_coral_fan", $factory->get(390, 1));
self::register("brewing_stand", $factory->get(117, 0));
self::register("brick_slab", $factory->get(44, 4));
self::register("brick_stairs", $factory->get(108, 0));
@ -723,6 +741,9 @@ final class VanillaBlocks{
self::register("brown_stained_clay", $factory->get(159, 12));
self::register("brown_stained_glass", $factory->get(241, 12));
self::register("brown_stained_glass_pane", $factory->get(160, 12));
self::register("bubble_coral", $factory->get(386, 2));
self::register("bubble_coral_fan", $factory->get(388, 2));
self::register("bubble_wall_coral_fan", $factory->get(391, 0));
self::register("cactus", $factory->get(81, 0));
self::register("cake", $factory->get(92, 0));
self::register("carpet", $factory->get(171, 0));
@ -926,6 +947,9 @@ final class VanillaBlocks{
self::register("farmland", $factory->get(60, 0));
self::register("fern", $factory->get(31, 2));
self::register("fire", $factory->get(51, 0));
self::register("fire_coral", $factory->get(386, 3));
self::register("fire_coral_fan", $factory->get(388, 3));
self::register("fire_wall_coral_fan", $factory->get(391, 1));
self::register("flower_pot", $factory->get(140, 0));
self::register("frosted_ice", $factory->get(207, 0));
self::register("furnace", $factory->get(61, 2));
@ -988,6 +1012,9 @@ final class VanillaBlocks{
self::register("hardened_yellow_stained_glass_pane", $factory->get(191, 4));
self::register("hay_bale", $factory->get(170, 0));
self::register("hopper", $factory->get(154, 0));
self::register("horn_coral", $factory->get(386, 4));
self::register("horn_coral_fan", $factory->get(388, 4));
self::register("horn_wall_coral_fan", $factory->get(392, 0));
self::register("ice", $factory->get(79, 0));
self::register("infested_chiseled_stone_brick", $factory->get(97, 5));
self::register("infested_cobblestone", $factory->get(97, 1));
@ -1222,6 +1249,9 @@ final class VanillaBlocks{
self::register("trapped_chest", $factory->get(146, 2));
self::register("tripwire", $factory->get(132, 0));
self::register("tripwire_hook", $factory->get(131, 0));
self::register("tube_coral", $factory->get(386, 0));
self::register("tube_coral_fan", $factory->get(388, 0));
self::register("tube_wall_coral_fan", $factory->get(390, 0));
self::register("underwater_torch", $factory->get(239, 5));
self::register("vines", $factory->get(106, 0));
self::register("wall_banner", $factory->get(177, 2));

View File

@ -0,0 +1,78 @@
<?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\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\bedrock\CoralTypeIdMap;
use pocketmine\item\Item;
use pocketmine\item\ItemFactory;
use pocketmine\item\ItemIds;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
final class WallCoralFan extends BaseCoral{
use HorizontalFacingTrait;
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = BlockDataSerializer::readCoralFacing($stateMeta >> 2);
$this->dead = ($stateMeta & BlockLegacyMetadata::CORAL_FAN_HANG_FLAG_DEAD) !== 0;
}
public function writeStateToMeta() : int{
return (BlockDataSerializer::writeCoralFacing($this->facing) << 2) | ($this->dead ? BlockLegacyMetadata::CORAL_FAN_HANG_FLAG_DEAD : 0);
}
public function getStateBitmask() : int{
return 0b1110;
}
public function asItem() : Item{
return ItemFactory::getInstance()->get(
$this->dead ? ItemIds::CORAL_FAN_DEAD : ItemIds::CORAL_FAN,
CoralTypeIdMap::getInstance()->toId($this->coralType)
);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
$axis = Facing::axis($face);
if(($axis !== Axis::X && $axis !== Axis::Z) || !$blockClicked->isSolid()){
return false;
}
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onNearbyBlockChange() : void{
$world = $this->pos->getWorld();
if(!$world->getBlock($this->pos->getSide(Facing::opposite($this->facing)))->isSolid()){
$world->useBreakOn($this->pos);
}else{
parent::onNearbyBlockChange();
}
}
}

View File

@ -77,6 +77,11 @@ class ItemFactory{
$this->register(new Clock(new ItemIdentifier(ItemIds::CLOCK, 0), "Clock"));
$this->register(new Clownfish(new ItemIdentifier(ItemIds::CLOWNFISH, 0), "Clownfish"));
$this->register(new Coal(new ItemIdentifier(ItemIds::COAL, 0), "Coal"));
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::CORAL_FAN, 0), VanillaBlocks::TUBE_CORAL_FAN(), VanillaBlocks::TUBE_WALL_CORAL_FAN()), true);
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::CORAL_FAN, 1), VanillaBlocks::BRAIN_CORAL_FAN(), VanillaBlocks::BRAIN_WALL_CORAL_FAN()), true);
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::CORAL_FAN, 2), VanillaBlocks::BUBBLE_CORAL_FAN(), VanillaBlocks::BUBBLE_WALL_CORAL_FAN()), true);
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::CORAL_FAN, 3), VanillaBlocks::FIRE_CORAL_FAN(), VanillaBlocks::FIRE_WALL_CORAL_FAN()), true);
$this->register(new ItemBlockWallOrFloor(new ItemIdentifier(ItemIds::CORAL_FAN, 4), VanillaBlocks::HORN_CORAL_FAN(), VanillaBlocks::HORN_WALL_CORAL_FAN()), true);
$this->register(new Coal(new ItemIdentifier(ItemIds::COAL, 1), "Charcoal"));
$this->register(new CocoaBeans(new ItemIdentifier(ItemIds::DYE, 3), "Cocoa Beans"));
$this->register(new Compass(new ItemIdentifier(ItemIds::COMPASS, 0), "Compass"));

File diff suppressed because one or more lines are too long