The same blocks, simply better. For PocketMine & forks.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Main.php 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. # ____ _ _ ____ _ _
  3. # | __ ) ___ | |_ | |_ ___ _ __ | __ ) | | ___ ___ | | __ ___
  4. # | _ \ / _ \ | __| | __| / _ \ | '__| | _ \ | | / _ \ / __| | |/ / / __|
  5. # | |_) | | __/ | |_ | |_ | __/ | | | |_) | | | | (_) | | (__ | < \__ \
  6. # |____/ \___| \__| \__| \___| |_| |____/ |_| \___/ \___| |_|\_\ |___/
  7. #
  8. # Extends your Minecraft PE blocks palette ! For PocketMine.
  9. namespace Ad5001\BetterBlocks;
  10. use pocketmine\event\Listener;
  11. use pocketmine\plugin\PluginBase;
  12. use pocketmine\Server;
  13. use pocketmine\Player;
  14. use pocketmine\tile\Tile;
  15. use pocketmine\item\Item;
  16. use pocketmine\inventory\ShapedRecipe;
  17. use pocketmine\math\Vector3;
  18. use pocketmine\block\Block;
  19. use pocketmine\event\block\BlockBreakEvent;
  20. use pocketmine\nbt\NBT;
  21. use pocketmine\nbt\tag\ListTag;
  22. use Ad5001\BetterBlocks\CustomBlockData\FallableTile;
  23. use Ad5001\BetterBlocks\CustomBlockData\GraveTile;
  24. use Ad5001\BetterBlocks\CustomBlockData\RedstonePoweringTile;
  25. use Ad5001\BetterBlocks\CustomBlockData\SoundHolderTile;
  26. use Ad5001\BetterBlocks\CustomBlockData\StickTile;
  27. use Ad5001\BetterBlocks\CustomBlockData\TrapTile;
  28. use Ad5001\BetterBlocks\tasks\AttractTask;
  29. use Ad5001\BetterBlocks\tasks\BlockRegenerateTask;
  30. use Ad5001\BetterBlocks\tasks\Drop2CraftTask;
  31. use Ad5001\BetterBlocks\tasks\SetVacuumTask;
  32. use Ad5001\BetterBlocks\tasks\StickTask;
  33. class Main extends PluginBase implements Listener {
  34. static $instance;
  35. const PMMP_INCOMPATIBLE = [
  36. "Vacuum Hoppers",
  37. "Sticky Slime Blocks",
  38. "Trappers"
  39. ];
  40. public function onEnable(){
  41. if(!$this->isCompatible()) {
  42. $this->getLogger()->notice("This plugin only has partial support of this PocketMine version ({$this->getServer()->getName()}) due to all the features an API missing. The following things will be deactivated: " . implode(", ", self::PMMP_INCOMPATIBLE));
  43. }
  44. self::$instance = $this;
  45. // Registering recipes
  46. $ep = Item::get(368, 0);
  47. // Sticky Slime block
  48. $ssb = Item::get(165, 0);
  49. $ssb->setCustomName("§rSticky Slime Block");
  50. $ssb->setNamedTag(NBT::parseJSON('{"isStickable":"true"}'));
  51. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($ssb, 3, 3))->addIngredient(1, 1, $ep)->addIngredient(1, 2, Item::get(165, 0)));
  52. // Vacuum Hopper
  53. $vh = Item::get(410, 0);
  54. $vh->setNamedTag(NBT::parseJSON('{"isVacuum":"true"'));
  55. $vh->setCustomName("§rVacuum Item Hopper");
  56. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($vh, 3, 3))->addIngredient(1, 0, $ep)->addIngredient(0, 0, Item::get(410, 0))->addIngredient(0, 1, Item::get(49, 0)));
  57. // Trappers (used to create trap blocks)
  58. $vh = Item::get(69, 0);
  59. $vh->setNamedTag(NBT::parseJSON('{"isTrapper":"true"}'));
  60. $vh->setCustomName("§rTrapper");
  61. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($vh, 3, 3))->addIngredient(1, 1, Item::get(70, 0))->addIngredient(0, 0, Item::get(331, 0)));
  62. // Hammes (for fallable blocks)
  63. // Wooden
  64. $wt = Item::get(271, 0);
  65. $wt->setNamedTag(NBT::parseJSON('{"isHammer":"true"}'));
  66. $wt->setCustomName("§rWooden Hammer");
  67. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($wt, 3, 3))->addIngredient(2, 0, Item::get(280, 0))->addIngredient(1, 1, Item::get(280, 0))->addIngredient(0, 1, Item::get(5, 0))->addIngredient(0, 2, Item::get(5, 0))->addIngredient(1, 2, Item::get(5, 0)));
  68. // Stone
  69. $wt = Item::get(275, 0);
  70. $wt->setNamedTag(NBT::parseJSON('{"isHammer":"true"}'));
  71. $wt->setCustomName("§rStone Hammer");
  72. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($wt, 3, 3))->addIngredient(2, 0, Item::get(280, 0))->addIngredient(1, 1, Item::get(280, 0))->addIngredient(0, 1, Item::get(4, 0))->addIngredient(0, 2, Item::get(4, 0))->addIngredient(1, 2, Item::get(4, 0)));
  73. // Iron
  74. $wt = Item::get(258, 0);
  75. $wt->setNamedTag(NBT::parseJSON('{"isHammer":"true"}'));
  76. $wt->setCustomName("§rIron Hammer");
  77. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($wt, 3, 3))->addIngredient(2, 0, Item::get(280, 0))->addIngredient(1, 1, Item::get(280, 0))->addIngredient(0, 1, Item::get(265, 0))->addIngredient(0, 2, Item::get(265, 0))->addIngredient(1, 2, Item::get(265, 0)));
  78. // Gold
  79. $wt = Item::get(286, 0);
  80. $wt->setNamedTag(NBT::parseJSON('{"isHammer":"true"}'));
  81. $wt->setCustomName("§rGold Hammer");
  82. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($wt, 3, 3))->addIngredient(2, 0, Item::get(280, 0))->addIngredient(1, 1, Item::get(280, 0))->addIngredient(0, 1, Item::get(266, 0))->addIngredient(0, 2, Item::get(266, 0))->addIngredient(1, 2, Item::get(266, 0)));
  83. // Diamond
  84. $wt = Item::get(279, 0);
  85. $wt->setNamedTag(NBT::parseJSON('{"isHammer":"true"}'));
  86. $wt->setCustomName("§rDiamond Hammer");
  87. $this->getServer()->getCraftingManager()->registerRecipe((new ShapedRecipe($wt, 3, 3))->addIngredient(2, 0, Item::get(280, 0))->addIngredient(1, 1, Item::get(280, 0))->addIngredient(0, 1, Item::get(264, 0))->addIngredient(0, 2, Item::get(264, 0))->addIngredient(1, 2, Item::get(264, 0)));
  88. // Registering tiles
  89. Tile::registerTile(FallableTile::class);
  90. Tile::registerTile(GraveTile::class);
  91. Tile::registerTile(RedstonePoweringTile::class);
  92. Tile::registerTile(SoundHolderTile::class);
  93. Tile::registerTile(TrapTile::class);
  94. if($this->isCompatible()) {
  95. Tile::registerTile(StickTile::class);
  96. }
  97. // Launch tasks
  98. $this->getServer()->getScheduler()->scheduleRepeatingTask(new Drop2CraftTask($this), 5);
  99. if($this->isCompatible()) { // Removes uncessery lag on PMMP
  100. $this->getServer()->getScheduler()->scheduleRepeatingTask(new AttractTask($this), 5);
  101. $this->getServer()->getScheduler()->scheduleRepeatingTask(new StickTask($this), 1);
  102. }
  103. $this->getServer()->getPluginManager()->registerEvents($this, $this);
  104. $this->saveDefaultConfig();
  105. }
  106. /*
  107. When a custom block item is placed (Sticcky Slime Blocks, Vaccum hoppers and for Trap Blocks)
  108. @param $event \pocketmine\event\block\BlockPlaceEvent
  109. */
  110. public function onBlockPlace(\pocketmine\event\block\BlockPlaceEvent $event) {
  111. if($this->isCompatible()) {
  112. if(isset($event->getItem()->getNamedTag()->isStickable) && $event->getItem()->getNamedTag()->isStickable->getValue() == "true") {
  113. Tile::createTile("StickTile", $event->getBlock()->getLevel()->getChunk($event->getBlock()->x >> 4, $event->getBlock()->z >> 4), NBT::parseJSON(json_encode(["x" => $event->getBlock()->x, "y" => $event->getBlock()->y, "z" => $event->getBlock()->z], JSON_FORCE_OBJECT)));
  114. } elseif(isset($event->getItem()->getNamedTag()->isVacuum) && $event->getItem()->getNamedTag()->isVacuum->getValue() == "true") {
  115. $this->getServer()->getScheduler()->scheduleRepeatingTask(new SetVacuumTask($this, $event->getBlock()), 1); // Tile gets created after the event so delaying it one 1 tick.
  116. } elseif(isset($event->getItem()->getNamedTag()->isTrapper) && $event->getItem()->getNamedTag()->isTrapper->getValue() == "true") {
  117. Tile::createTile("TrapTile", $event->getBlock()->getLevel()->getChunk($event->getBlock()->x >> 4, $event->getBlock()->z >> 4), NBT::parseJSON(json_encode(["x" => $event->getBlock()->x, "y" => $event->getBlock()->y - 1, "z" => $event->getBlock()->z], JSON_FORCE_OBJECT)));
  118. $this->getServer()->getScheduler()->scheduleDelayedTask(new BlockRegenerateTask($this, Block::get(0, 0), $event->getBlock()->getLevel()), 30); // Clears the lever
  119. }
  120. }
  121. }
  122. /*
  123. Checks when a block breaks to drop the item if custom block.
  124. @param $event BlockBreakEvent
  125. */
  126. public function onBlockBreak(BlockBreakEvent $event) {
  127. //1
  128. $block = $event->getBlock();
  129. if($event->getBlock()->getLevel()->getTile($event->getBlock()) instanceof CustomBlockData && !$event->isCancelled()) {
  130. switch(get_class($event->getBlock()->getLevel()->getTile($event->getBlock()))) {
  131. case "Ad5001\\BetterBlocks\\CustomBlockData\\GraveTile":
  132. $event->getBlock()->getLevel()->getTile($event->getBlock())->drop();
  133. $event->getBlock()->getLevel()->removeTile($event->getBlock()->getLevel()->getTile($event->getBlock()));
  134. break;
  135. case "Ad5001\\BetterBlocks\\CustomBlockData\\RedstonePoweringTile":
  136. $redstoneblock = Item::get(152, 0);
  137. $event->setDrops(array_merge([$redstoneblock], $event->getDrops()));
  138. $event->getBlock()->getLevel()->removeTile($event->getBlock()->getLevel()->getTile($event->getBlock()));
  139. break;
  140. case "Ad5001\\BetterBlocks\\CustomBlockData\\SoundHolderTile":
  141. $noteblock = Item::get(25, 0);
  142. $event->setDrops(array_merge([$noteblock], $event->getDrops()));
  143. $event->getBlock()->getLevel()->removeTile($event->getBlock()->getLevel()->getTile($event->getBlock()));
  144. break;
  145. case "Ad5001\\BetterBlocks\\CustomBlockData\\StickTile":
  146. $ssb = Item::get(165, 0);
  147. $ssb->setCustomName("§rSticky Slime Block");
  148. $ssb->setNamedTag(NBT::parseJSON('{"isStickable":"true"}'));
  149. $event->setDrops([$ssb]);
  150. $event->getBlock()->getLevel()->removeTile($event->getBlock()->getLevel()->getTile($event->getBlock()));
  151. break;
  152. case "Ad5001\\BetterBlocks\\CustomBlockData\\TrapTile":
  153. $event->getBlock()->getLevel()->removeTile($event->getBlock()->getLevel()->getTile($event->getBlock()));
  154. break;
  155. case "Ad5001\\BetterBlocks\\CustomBlockData\\VacuumTile":
  156. $vh = Item::get(410, 0);
  157. $vh->setNamedTag(NBT::parseJSON('{"isVacuum":"true"}'));
  158. $vh->setCustomName("§rVacuum Item Hopper");
  159. $event->setDrops([$vh]);
  160. $event->getBlock()->getLevel()->removeTile($event->getBlock()->getLevel()->getTile($event->getBlock()));
  161. break;
  162. case "Ad5001\\BetterBlocks\\CustomBlockData\\FallableTile":
  163. $event->getBlock()->getLevel()->removeTile($event->getBlock()->getLevel()->getTile($event->getBlock()));
  164. break;
  165. }
  166. }
  167. }
  168. /*
  169. Check if a player touches a block. Check if he right clicks ill a hammer or will change the sound of a sound holder block.
  170. @param $event \pocketmine\event\player\PlayerInteractEvent
  171. */
  172. public function onInteract(\pocketmine\event\player\PlayerInteractEvent $event) {
  173. $this->getLogger()->info(get_class($event->getBlock()->getLevel()->getTile($event->getBlock())));
  174. if(isset($event->getItem()->getNamedTag()->isHammer) && $event->getItem()->getNamedTag()->isHammer == "true" && !($event->getBlock() instanceof \pocketmine\block\Fallable) && !($event->getBlock()->getLevel()->getTile($event->getBlock()) instanceof Tile)) {
  175. Tile::createTile("FallableTile", $event->getBlock()->getLevel()->getChunk($event->getBlock()->x >> 4, $event->getBlock()->z >> 4), NBT::parseJSON('{"x":"'.$event->getBlock()->x.'","y":"' .$event->getBlock()->y.'","z":"'. $event->getBlock()->z . '"}'));
  176. $event->getPlayer()->sendPopup("This block seems now unstable... You shouldn't walk on it...");
  177. } elseif($event->getBlock()->getLevel()->getTile($event->getBlock()) instanceof SoundHolderTile) {
  178. $s = SoundHolderTile::SOUNDS[rand(0, count(SoundHolderTile::SOUNDS) - 1)];
  179. $event->getBlock()->getLevel()->getTile($event->getBlock())->setSound($s);
  180. $event->getPlayer()->sendPopup("Sound has changed to $s.");
  181. }
  182. }
  183. /*
  184. Detects when a player drops an item to set it as "Droped by the player" to build some custom blocks.
  185. @param $event \pocketmine\event\player\PlayerDropItemEvent
  186. */
  187. public function onPlayerItemDrop(\pocketmine\event\player\PlayerDropItemEvent $event) {
  188. $tag = $event->getItem()->getNamedTag();
  189. if(is_null($tag)) {
  190. $tag = NBT::parseJSON('{"isDropedByPlayer":"true"}');
  191. } else {
  192. $tag->isDropedByPlayer = new \pocketmine\nbt\tag\StringTag("true");
  193. }
  194. $event->getItem()->setNamedTag($tag);
  195. }
  196. /*
  197. Checks when a player death to set up a grave.
  198. @param $event \pocketmine\event\player\PlayerDeathEvent
  199. */
  200. public function onPlayerDeath(\pocketmine\event\player\PlayerDeathEvent $event) {
  201. $player = $event->getPlayer();
  202. $cause = $player->getLastDamageCause();
  203. $nbt = NBT::parseJSON(json_encode([
  204. "x" => $player->x,
  205. "y" => $player->y,
  206. "z" => $player->z,
  207. "Player" => $player->getName(),
  208. "DeathCause" => $cause == null ? 14 : $cause->getCause(),
  209. "Params" => $event->getDeathMessage()->getParameters()
  210. ], JSON_FORCE_OBJECT));
  211. $nbt->Items = new ListTag("Items", []);
  212. $nbt->Items->setTagType(NBT::TAG_Compound);
  213. foreach($event->getDrops() as $index => $content) {
  214. $nbt->Items[$index] = $content->nbtSerialize();
  215. }
  216. $event->setDrops([]);
  217. Tile::createTile("GraveTile", $player->getLevel()->getChunk($player->x >> 4, $player->z >> 4), $nbt);
  218. }
  219. /*
  220. Checks when a player moves to 1) Trigger traps blocks 2) Makes fallable block fall if a player walks on it. 3) Play a sound when a player walks on it.
  221. @param $event \pocketmine\event\player\PlayerMoveEvent
  222. */
  223. public function onPlayerMove(\pocketmine\event\player\PlayerMoveEvent $event) {
  224. $v3Under = $event->getPlayer()->round();
  225. $v3Under->y--;
  226. $v3Upper = $event->getPlayer()->round();
  227. $v3Upper->y += 2;
  228. $tileUnder = $event->getPlayer()->getLevel()->getTile($v3Under);
  229. $tileUpper = $event->getPlayer()->getLevel()->getTile($v3Upper);
  230. // 1)
  231. if($tileUnder instanceof TrapTile) {
  232. $b = clone $event->getPlayer()->getLevel()->getBlock($v3Under);
  233. $event->getPlayer()->getLevel()->setBlock($v3Under, Block::get(0, 0));
  234. $this->getServer()->getScheduler()->scheduleDelayedTask(new BlockRegenerateTask($this, $b, $event->getPlayer()->getLevel()), 30);
  235. }
  236. // 2)
  237. if($tileUnder instanceof FallableTile) {
  238. $tileUnder->fall();
  239. }
  240. // 3)
  241. if($tileUnder instanceof SoundHolderTile) {
  242. $tileUnder->play();
  243. }
  244. }
  245. /*
  246. Cheks compatibilty issues for PMMP and other uncompatible software.
  247. @return bool
  248. */
  249. public function isCompatible() : bool {
  250. return class_exists("pocketmine\\tile\\Hopper") && class_exists("pocketmine\\block\\SlimeBlock") && class_exists("pocketmine\\item\\Lever");
  251. }
  252. }