setPermission(DefaultPermissionNames::COMMAND_PARTICLE); } public function execute(CommandSender $sender, string $commandLabel, array $args){ if(count($args) < 7){ throw new InvalidCommandSyntaxException(); } if($sender instanceof Player){ $senderPos = $sender->getPosition(); $world = $senderPos->getWorld(); $pos = new Vector3( $this->getRelativeDouble($senderPos->getX(), $sender, $args[1]), $this->getRelativeDouble($senderPos->getY(), $sender, $args[2], World::Y_MIN, World::Y_MAX), $this->getRelativeDouble($senderPos->getZ(), $sender, $args[3]) ); }else{ $world = $sender->getServer()->getWorldManager()->getDefaultWorld(); $pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]); } $name = strtolower($args[0]); $xd = (float) $args[4]; $yd = (float) $args[5]; $zd = (float) $args[6]; $count = isset($args[7]) ? max(1, (int) $args[7]) : 1; $data = $args[8] ?? null; $particle = $this->getParticle($name, $data); if($particle === null){ $sender->sendMessage(KnownTranslationFactory::commands_particle_notFound($name)->prefix(TextFormat::RED)); return true; } $sender->sendMessage(KnownTranslationFactory::commands_particle_success($name, (string) $count)); $random = new Random((int) (microtime(true) * 1000) + mt_rand()); for($i = 0; $i < $count; ++$i){ $world->addParticle($pos->add( $random->nextSignedFloat() * $xd, $random->nextSignedFloat() * $yd, $random->nextSignedFloat() * $zd ), $particle); } return true; } private function getParticle(string $name, ?string $data = null) : ?Particle{ switch($name){ case "explode": return new ExplodeParticle(); case "hugeexplosion": return new HugeExplodeParticle(); case "hugeexplosionseed": return new HugeExplodeSeedParticle(); case "bubble": return new BubbleParticle(); case "splash": return new SplashParticle(); case "wake": case "water": return new WaterParticle(); case "crit": return new CriticalParticle(); case "smoke": return new SmokeParticle((int) ($data ?? 0)); case "spell": return new EnchantParticle(new Color(0, 0, 0, 255)); //TODO: colour support case "instantspell": return new InstantEnchantParticle(new Color(0, 0, 0, 255)); //TODO: colour support case "dripwater": return new WaterDripParticle(); case "driplava": return new LavaDripParticle(); case "townaura": case "spore": return new SporeParticle(); case "portal": return new PortalParticle(); case "flame": return new FlameParticle(); case "lava": return new LavaParticle(); case "reddust": return new RedstoneParticle((int) ($data ?? 1)); case "snowballpoof": return new ItemBreakParticle(VanillaItems::SNOWBALL()); case "slime": return new ItemBreakParticle(VanillaItems::SLIMEBALL()); case "itembreak": if($data !== null){ $item = StringToItemParser::getInstance()->parse($data); if($item !== null && !$item->isNull()){ return new ItemBreakParticle($item); } } break; case "terrain": if($data !== null){ $block = StringToItemParser::getInstance()->parse($data)?->getBlock(); if($block !== null && $block->getTypeId() !== BlockTypeIds::AIR){ return new TerrainParticle($block); } } break; case "heart": return new HeartParticle((int) ($data ?? 0)); case "ink": return new InkParticle((int) ($data ?? 0)); case "droplet": return new RainSplashParticle(); case "enchantmenttable": return new EnchantmentTableParticle(); case "happyvillager": return new HappyVillagerParticle(); case "angryvillager": return new AngryVillagerParticle(); case "forcefield": return new BlockForceFieldParticle((int) ($data ?? 0)); case "mobflame": return new EntityFlameParticle(); case "iconcrack": if($data !== null && ($item = StringToItemParser::getInstance()->parse($data)) !== null && !$item->isNull()){ return new ItemBreakParticle($item); } break; case "blockcrack": if($data !== null && ($block = StringToItemParser::getInstance()->parse($data)?->getBlock()) !== null && $block->getTypeId() !== BlockTypeIds::AIR){ return new TerrainParticle($block); } break; case "blockdust": if($data !== null){ //to preserve the old unlimited explode behaviour, allow this to split into at most 5 parts //this allows the 4th argument to be processed normally if given without forcing it to also consume //any unexpected parts //we probably ought to error in this case, but this will do for now $d = explode("_", $data, limit: 5); if(count($d) >= 3){ return new DustParticle(new Color( ((int) $d[0]) & 0xff, ((int) $d[1]) & 0xff, ((int) $d[2]) & 0xff, ((int) ($d[3] ?? 255)) & 0xff )); } } break; } return null; } }