From 582c16547943a0e275f88d381204f43791e19e46 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Tue, 7 Oct 2014 17:46:01 +0200 Subject: [PATCH] Implemented Explosion and PrimedTNT, closes #2139 --- src/pocketmine/Server.php | 2 +- src/pocketmine/block/Block.php | 5 +- src/pocketmine/block/Fallable.php | 1 - src/pocketmine/block/TNT.php | 37 ++++++-- src/pocketmine/entity/DroppedItem.php | 3 +- src/pocketmine/entity/Entity.php | 8 +- src/pocketmine/entity/Explosive.php | 1 + src/pocketmine/entity/PrimedTNT.php | 114 ++++++++++++++++++++++++ src/pocketmine/level/Explosion.php | 110 ++++++++++++++++------- src/pocketmine/level/Level.php | 4 +- src/pocketmine/plugin/PluginManager.php | 4 - 11 files changed, 237 insertions(+), 52 deletions(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 912d8ae93..a0cf75770 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1584,7 +1584,7 @@ public function __construct(\ClassLoader $autoloader, \ThreadedLogger $logger, $ Generator::addGenerator(Normal::class, "default"); //Temporal workaround, pthreads static property nullification test - if(PluginManager::$pluginParentTimer === null){ + if(PluginManager::$pluginParentTimer === null or Timings::$serverTickTimer === null){ $this->getLogger()->emergency("You are using an invalid pthreads version. Please update your binaries."); kill(getmypid()); return; diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index a62f28d89..c22273b46 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -687,7 +687,10 @@ public static function get($id, $meta = 0, Position $pos = null){ } if($pos instanceof Position){ - $block->position($pos); + $block->x = $pos->x; + $block->y = $pos->y; + $block->z = $pos->z; + $block->level = $pos->level; } return $block; diff --git a/src/pocketmine/block/Fallable.php b/src/pocketmine/block/Fallable.php index faefe002b..508c4543e 100644 --- a/src/pocketmine/block/Fallable.php +++ b/src/pocketmine/block/Fallable.php @@ -51,7 +51,6 @@ public function onUpdate($type){ new Double("", $this->y + 0.5), new Double("", $this->z + 0.5) ]), - //TODO: add random motion with physics "Motion" => new Enum("Motion", [ new Double("", 0), new Double("", 0), diff --git a/src/pocketmine/block/TNT.php b/src/pocketmine/block/TNT.php index 0e52a8dcf..5845aa3cb 100644 --- a/src/pocketmine/block/TNT.php +++ b/src/pocketmine/block/TNT.php @@ -21,8 +21,16 @@ namespace pocketmine\block; +use pocketmine\entity\PrimedTNT; use pocketmine\item\Item; +use pocketmine\level\Explosion; +use pocketmine\nbt\tag\Byte; +use pocketmine\nbt\tag\Compound; +use pocketmine\nbt\tag\Double; +use pocketmine\nbt\tag\Enum; +use pocketmine\nbt\tag\Float; use pocketmine\Player; +use pocketmine\utils\Random; class TNT extends Solid{ public function __construct(){ @@ -33,9 +41,8 @@ public function __construct(){ public function onActivate(Item $item, Player $player = null){ if($item->getID() === Item::FLINT_STEEL){ - if(($player->gamemode & 0x01) === 0){ - $item->useOn($this); - } + $item->useOn($this); + $data = [ "x" => $this->x + 0.5, "y" => $this->y + 0.5, @@ -44,9 +51,27 @@ public function onActivate(Item $item, Player $player = null){ "fuse" => 20 * 4, //4 seconds ]; $this->getLevel()->setBlock($this, new Air(), false, false, true); - //TODO - //$e = Server::getInstance()->api->entity->add($this->level, ENTITY_OBJECT, OBJECT_PRIMEDTNT, $data); - //$e->spawnToAll(); + + $mot = (new Random())->nextSignedFloat() * M_PI * 2; + $tnt = new PrimedTNT($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new Compound("", [ + "Pos" => new Enum("Pos", [ + new Double("", $this->x + 0.5), + new Double("", $this->y + 0.5), + new Double("", $this->z + 0.5) + ]), + "Motion" => new Enum("Motion", [ + new Double("", -sin($mot) * 0.02), + new Double("", 0.2), + new Double("", -cos($mot) * 0.02) + ]), + "Rotation" => new Enum("Rotation", [ + new Float("", 0), + new Float("", 0) + ]), + "Fuse" => new Byte("Fuse", 80) + ])); + + $tnt->spawnToAll(); return true; } diff --git a/src/pocketmine/entity/DroppedItem.php b/src/pocketmine/entity/DroppedItem.php index 8968d1ae1..3cf449c8b 100644 --- a/src/pocketmine/entity/DroppedItem.php +++ b/src/pocketmine/entity/DroppedItem.php @@ -97,6 +97,8 @@ public function onUpdate(){ $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; + $this->updateMovement(); + if($this->onGround){ $this->motionY *= -0.5; } @@ -105,7 +107,6 @@ public function onUpdate(){ $this->kill(); } - $this->updateMovement(); } $this->timings->stopTiming(); diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index fe16ee129..4b1e68462 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -281,10 +281,10 @@ public function sendMetadata($player){ foreach($player as $p){ if($p === $this){ /** @var Player $p */ - $pk = new SetEntityDataPacket(); - $pk->eid = 0; - $pk->metadata = $this->getData(); - $p->dataPacket($pk); + $pk2 = new SetEntityDataPacket(); + $pk2->eid = 0; + $pk2->metadata = $this->getData(); + $p->dataPacket($pk2); }else{ $p->dataPacket($pk); } diff --git a/src/pocketmine/entity/Explosive.php b/src/pocketmine/entity/Explosive.php index d728861cc..8ef853314 100644 --- a/src/pocketmine/entity/Explosive.php +++ b/src/pocketmine/entity/Explosive.php @@ -24,4 +24,5 @@ interface Explosive{ + public function explode(); } \ No newline at end of file diff --git a/src/pocketmine/entity/PrimedTNT.php b/src/pocketmine/entity/PrimedTNT.php index 73dc9b514..2e6627d99 100644 --- a/src/pocketmine/entity/PrimedTNT.php +++ b/src/pocketmine/entity/PrimedTNT.php @@ -22,10 +22,124 @@ namespace pocketmine\entity; +use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\level\Explosion; +use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\String; +use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\protocol\SetEntityMotionPacket; +use pocketmine\Player; class PrimedTNT extends Entity implements Explosive{ + const NETWORK_ID = 65; + + public $width = 0.98; + public $length = 0.98; + public $height = 0.98; + + protected $gravity = 0.04; + protected $drag = 0.02; + + protected $fuse; + + public $canCollide = false; + protected function initEntity(){ $this->namedtag->id = new String("id", "PrimedTNT"); + if(isset($this->namedtag->Fuse)){ + $this->fuse = $this->namedtag["Fuse"]; + }else{ + $this->fuse = 80; + } + } + + + public function canCollideWith(Entity $entity){ + return false; + } + + public function getData(){ + return [ + 16 => ["type" => 0, "value" => $this->fuse], + ]; + } + + public function saveNBT(){ + parent::saveNBT(); + $this->namedtag->Fuse = new Byte("Fuse", $this->fuse); + } + + public function onUpdate(){ + + if($this->closed){ + return false; + } + + $this->timings->startTiming(); + + $this->entityBaseTick(); + + if(!$this->dead){ + + $this->motionY -= $this->gravity; + + $this->move($this->motionX, $this->motionY, $this->motionZ); + + $friction = 1 - $this->drag; + + $this->motionX *= $friction; + $this->motionY *= $friction; + $this->motionZ *= $friction; + + $this->updateMovement(); + + if($this->onGround){ + $this->motionY *= -0.5; + $this->motionX *= 0.7; + $this->motionZ *= 0.7; + } + + if($this->fuse-- <= 0){ + $this->kill(); + $this->explode(); + }else{ + $this->sendMetadata($this->getViewers()); + } + + } + + + return !$this->onGround or ($this->motionX == 0 and $this->motionY == 0 and $this->motionZ == 0); + } + + public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){ + + } + + public function heal($amount){ + + } + + public function explode(){ + (new Explosion($this, 4, $this))->explode(); + } + + public function spawnTo(Player $player){ + $pk = new AddEntityPacket(); + $pk->type = PrimedTNT::NETWORK_ID; + $pk->eid = $this->getID(); + $pk->x = $this->x; + $pk->y = $this->y; + $pk->z = $this->z; + $pk->did = 0; + $player->dataPacket($pk); + + $pk = new SetEntityMotionPacket(); + $pk->entities = [ + [$this->getID(), $this->motionX, $this->motionY, $this->motionZ] + ]; + $player->dataPacket($pk); + + parent::spawnTo($player); } } \ No newline at end of file diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index b2b9f9ab3..8f5bd24f7 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -24,20 +24,24 @@ use pocketmine\block\Block; use pocketmine\block\TNT; use pocketmine\entity\Entity; +use pocketmine\entity\PrimedTNT; +use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityExplodeEvent; use pocketmine\item\Item; +use pocketmine\math\AxisAlignedBB; +use pocketmine\math\Math; use pocketmine\math\Vector3 as Vector3; +use pocketmine\nbt\tag\Byte; +use pocketmine\nbt\tag\Compound; +use pocketmine\nbt\tag\Double; +use pocketmine\nbt\tag\Enum; +use pocketmine\nbt\tag\Float; use pocketmine\network\protocol\ExplodePacket; use pocketmine\Server; +use pocketmine\utils\Random; class Explosion{ - public static $specialDrops = [ - Item::GRASS => Item::DIRT, - Item::STONE => Item::COBBLESTONE, - Item::COAL_ORE => Item::COAL, - Item::DIAMOND_ORE => Item::DIAMOND, - Item::REDSTONE_ORE => Item::REDSTONE, - ]; + private $rays = 16; //Rays public $level; public $source; @@ -47,6 +51,7 @@ class Explosion{ */ public $affectedBlocks = []; public $stepLen = 0.3; + /** @var Entity|Block|Tile */ private $what; public function __construct(Position $center, $size, $what = null){ @@ -56,6 +61,9 @@ public function __construct(Position $center, $size, $what = null){ $this->what = $what; } + /** + * @return bool + */ public function explode(){ if($this->size < 0.1){ return false; @@ -64,6 +72,7 @@ public function explode(){ $mRays = $this->rays - 1; for($i = 0; $i < $this->rays; ++$i){ for($j = 0; $j < $this->rays; ++$j){ + //break 2 gets here for($k = 0; $k < $this->rays; ++$k){ if($i == 0 or $i == $mRays or $j == 0 or $j == $mRays or $k == 0 or $k == $mRays){ $vector = new Vector3($i / $mRays * 2 - 1, $j / $mRays * 2 - 1, $k / $mRays * 2 - 1); //($i / $mRays) * 2 - 1 @@ -72,6 +81,9 @@ public function explode(){ for($blastForce = $this->size * (mt_rand(700, 1300) / 1000); $blastForce > 0; $blastForce -= $this->stepLen * 0.75){ $vBlock = $pointer->floor(); + if($vBlock->y < 0 or $vBlock->y > 127){ + break; + } $blockID = $this->level->getBlockIdAt($vBlock->x, $vBlock->y, $vBlock->z); if($blockID > 0){ @@ -96,11 +108,10 @@ public function explode(){ $send = []; $source = $this->source->floor(); - $radius = 2 * $this->size; $yield = (1 / $this->size) * 100; if($this->what instanceof Entity){ - Server::getInstance()->getPluginManager()->callEvent($ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield)); + $this->level->getServer()->getPluginManager()->callEvent($ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield)); if($ev->isCancelled()){ return false; }else{ @@ -109,33 +120,66 @@ public function explode(){ } } - //TODO - /*foreach($server->api->entity->getRadius($this->source, $radius) as $entity){ - $impact = (1 - $this->source->distance($entity) / $radius) * 0.5; //placeholder, 0.7 should be exposure - $damage = (int) (($impact * $impact + $impact) * 8 * $this->size + 1); - $entity->harm($damage, "explosion"); - }*/ + $explosionSize = $this->size * 2; + $minX = Math::floorFloat($this->source->x - $explosionSize - 1); + $maxX = Math::floorFloat($this->source->x + $explosionSize + 1); + $minY = Math::floorFloat($this->source->y - $explosionSize - 1); + $maxY = Math::floorFloat($this->source->y + $explosionSize + 1); + $minZ = Math::floorFloat($this->source->z - $explosionSize - 1); + $maxZ = Math::floorFloat($this->source->z + $explosionSize + 1); + + $explosionBB = new AxisAlignedBB($minX, $minY, $minZ, $maxX, $maxY, $maxZ); + + $list = $this->level->getNearbyEntities($explosionBB, $this->what instanceof Entity ? $this->what : null); + foreach($list as $entity){ + $distance = $entity->distance($this->source) / $explosionSize; + + if($distance <= 1){ + $motion = $entity->subtract($this->source)->normalize(); + + $impact = (1 - $distance) * ($exposure = 1); + + $damage = (int) ((($impact * $impact + $impact) / 2) * 8 * $explosionSize + 1); + + $this->level->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($entity, $this->what instanceof Entity ? EntityDamageEvent::CAUSE_ENTITY_EXPLOSION : EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, $damage)); + + if(!$ev->isCancelled()){ + $entity->attack($ev->getFinalDamage(), $ev); + $entity->setMotion($motion->multiply($impact)); + } + + } + } + + $air = Item::get(Item::AIR); foreach($this->affectedBlocks as $block){ + $block->setDamage($this->level->getBlockDataAt($block->x, $block->y, $block->z)); + if($block instanceof TNT){ - $data = [ - "x" => $block->x + 0.5, - "y" => $block->y + 0.5, - "z" => $block->z + 0.5, - "power" => 4, - "fuse" => mt_rand(10, 30), //0.5 to 1.5 seconds - ]; - //TODO - //$e = $server->api->entity->add($this->level, ENTITY_OBJECT, OBJECT_PRIMEDTNT, $data); - //$e->spawnToAll(); + $mot = (new Random())->nextSignedFloat() * M_PI * 2; + $tnt = new PrimedTNT($this->level->getChunk($block->x >> 4, $block->z >> 4), new Compound("", [ + "Pos" => new Enum("Pos", [ + new Double("", $block->x + 0.5), + new Double("", $block->y + 0.5), + new Double("", $block->z + 0.5) + ]), + "Motion" => new Enum("Motion", [ + new Double("", -sin($mot) * 0.02), + new Double("", 0.2), + new Double("", -cos($mot) * 0.02) + ]), + "Rotation" => new Enum("Rotation", [ + new Float("", 0), + new Float("", 0) + ]), + "Fuse" => new Byte("Fuse", mt_rand(10, 30)) + ])); + $tnt->spawnToAll(); }elseif(mt_rand(0, 100) < $yield){ - if(isset(self::$specialDrops[$block->getID()])){ - //TODO - //$server->api->entity->drop(new Position($block->x + 0.5, $block->y, $block->z + 0.5, $this->level), Item::get(self::$specialDrops[$block->getID()], 0)); - }else{ - //TODO - //$server->api->entity->drop(new Position($block->x + 0.5, $block->y, $block->z + 0.5, $this->level), Item::get($block->getID(), $this->level->level->getBlockDamage($block->x, $block->y, $block->z))); + foreach($block->getDrops($air) as $drop){ + $this->level->dropItem($block, Item::get(...$drop)); } } $this->level->setBlockIdAt($block->x, $block->y, $block->z, 0); @@ -147,7 +191,9 @@ public function explode(){ $pk->z = $this->source->z; $pk->radius = $this->size; $pk->records = $send; - Server::broadcastPacket($this->level->getPlayers(), $pk); + Server::broadcastPacket($this->level->getUsingChunk($source->x >> 4, $source->z >> 4), $pk); + + return true; } } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index b1af6dfed..a3d27afe7 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -883,7 +883,7 @@ public function getBlock(Vector3 $pos){ return $air; } - return Block::get($blockId, $meta, Position::fromObject($pos, $this)); + return Block::get($blockId, $meta, new Position($pos->x, $pos->y, $pos->z, $this)); } /** @@ -1079,7 +1079,7 @@ public function useBreakOn(Vector3 $vector, Item &$item = null, Player $player = if(!($player instanceof Player) or ($player->getGamemode() & 0x01) === 0){ foreach($drops as $drop){ if($drop[2] > 0){ - $this->dropItem($vector->add(0.5, 0.5, 0.5), Item::get($drop[0], $drop[1], $drop[2])); + $this->dropItem($vector->add(0.5, 0.5, 0.5), Item::get(...$drop)); } } } diff --git a/src/pocketmine/plugin/PluginManager.php b/src/pocketmine/plugin/PluginManager.php index fac195377..fb59718bc 100644 --- a/src/pocketmine/plugin/PluginManager.php +++ b/src/pocketmine/plugin/PluginManager.php @@ -641,10 +641,6 @@ public function clearPlugins(){ * @param Event $event */ public function callEvent(Event $event){ - $this->fireEvent($event); - } - - private function fireEvent(Event $event){ $handlers = $event->getHandlers(); $listeners = $handlers->getRegisteredListeners();