mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-23 00:55:57 +00:00
Implemented Explosion and PrimedTNT, closes #2139
This commit is contained in:
parent
5fb205493a
commit
582c165479
@ -1584,7 +1584,7 @@ class Server{
|
||||
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;
|
||||
|
@ -687,7 +687,10 @@ abstract class Block extends Position implements Metadatable{
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -51,7 +51,6 @@ abstract class Fallable extends Solid{
|
||||
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),
|
||||
|
@ -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 @@ class TNT extends Solid{
|
||||
|
||||
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 @@ class TNT extends Solid{
|
||||
"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;
|
||||
}
|
||||
|
@ -97,6 +97,8 @@ class DroppedItem extends Entity{
|
||||
$this->motionY *= 1 - $this->drag;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
$this->updateMovement();
|
||||
|
||||
if($this->onGround){
|
||||
$this->motionY *= -0.5;
|
||||
}
|
||||
@ -105,7 +107,6 @@ class DroppedItem extends Entity{
|
||||
$this->kill();
|
||||
}
|
||||
|
||||
$this->updateMovement();
|
||||
}
|
||||
|
||||
$this->timings->stopTiming();
|
||||
|
@ -281,10 +281,10 @@ abstract class Entity extends Position implements Metadatable{
|
||||
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);
|
||||
}
|
||||
|
@ -24,4 +24,5 @@ namespace pocketmine\entity;
|
||||
|
||||
interface Explosive{
|
||||
|
||||
public function explode();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -24,20 +24,24 @@ namespace pocketmine\level;
|
||||
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 @@ class Explosion{
|
||||
$this->what = $what;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function explode(){
|
||||
if($this->size < 0.1){
|
||||
return false;
|
||||
@ -64,6 +72,7 @@ class Explosion{
|
||||
$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 @@ class Explosion{
|
||||
|
||||
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 @@ class Explosion{
|
||||
|
||||
$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 @@ class Explosion{
|
||||
}
|
||||
}
|
||||
|
||||
//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 @@ class Explosion{
|
||||
$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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -883,7 +883,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
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 @@ class Level implements ChunkManager, Metadatable{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -641,10 +641,6 @@ class PluginManager{
|
||||
* @param Event $event
|
||||
*/
|
||||
public function callEvent(Event $event){
|
||||
$this->fireEvent($event);
|
||||
}
|
||||
|
||||
private function fireEvent(Event $event){
|
||||
$handlers = $event->getHandlers();
|
||||
$listeners = $handlers->getRegisteredListeners();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user