PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
Explosion.php
1 <?php
2 
3 /*
4  *
5  * ____ _ _ __ __ _ __ __ ____
6  * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7  * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8  * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9  * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * @author PocketMine Team
17  * @link http://www.pocketmine.net/
18  *
19  *
20 */
21 
22 namespace pocketmine\level;
23 
44 
45 class Explosion{
46 
47  private $rays = 16; //Rays
48  public $level;
49  public $source;
50  public $size;
54  public $affectedBlocks = [];
55  public $stepLen = 0.3;
57  private $what;
58 
59  public function __construct(Position $center, $size, $what = null){
60  $this->level = $center->getLevel();
61  $this->source = $center;
62  $this->size = max($size, 0);
63  $this->what = $what;
64  }
65 
70  public function explode(){
71  if($this->explodeA()){
72  return $this->explodeB();
73  }
74 
75  return false;
76  }
77 
81  public function explodeA(){
82  if($this->size < 0.1){
83  return false;
84  }
85 
86  $vector = new Vector3(0, 0, 0);
87  $vBlock = new Vector3(0, 0, 0);
88 
89  $mRays = intval($this->rays - 1);
90  for($i = 0; $i < $this->rays; ++$i){
91  for($j = 0; $j < $this->rays; ++$j){
92  for($k = 0; $k < $this->rays; ++$k){
93  if($i === 0 or $i === $mRays or $j === 0 or $j === $mRays or $k === 0 or $k === $mRays){
94  $vector->setComponents($i / $mRays * 2 - 1, $j / $mRays * 2 - 1, $k / $mRays * 2 - 1);
95  $vector->setComponents(($vector->x / ($len = $vector->length())) * $this->stepLen, ($vector->y / $len) * $this->stepLen, ($vector->z / $len) * $this->stepLen);
96  $pointerX = $this->source->x;
97  $pointerY = $this->source->y;
98  $pointerZ = $this->source->z;
99 
100  for($blastForce = $this->size * (mt_rand(700, 1300) / 1000); $blastForce > 0; $blastForce -= $this->stepLen * 0.75){
101  $x = (int) $pointerX;
102  $y = (int) $pointerY;
103  $z = (int) $pointerZ;
104  $vBlock->x = $pointerX >= $x ? $x : $x - 1;
105  $vBlock->y = $pointerY >= $y ? $y : $y - 1;
106  $vBlock->z = $pointerZ >= $z ? $z : $z - 1;
107  if($vBlock->y < 0 or $vBlock->y > 127){
108  break;
109  }
110  $block = $this->level->getBlock($vBlock);
111 
112  if($block->getId() !== 0){
113  $blastForce -= ($block->getHardness() / 5 + 0.3) * $this->stepLen;
114  if($blastForce > 0){
115  if(!isset($this->affectedBlocks[$index = Level::blockHash($block->x, $block->y, $block->z)])){
116  $this->affectedBlocks[$index] = $block;
117  }
118  }
119  }
120  $pointerX += $vector->x;
121  $pointerY += $vector->y;
122  $pointerZ += $vector->z;
123  }
124  }
125  }
126  }
127  }
128 
129  return true;
130  }
131 
132  public function explodeB(){
133  $send = [];
134  $source = (new Vector3($this->source->x, $this->source->y, $this->source->z))->floor();
135  $yield = (1 / $this->size) * 100;
136 
137  if($this->what instanceof Entity){
138  $this->level->getServer()->getPluginManager()->callEvent($ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield));
139  if($ev->isCancelled()){
140  return false;
141  }else{
142  $yield = $ev->getYield();
143  $this->affectedBlocks = $ev->getBlockList();
144  }
145  }
146 
147  $explosionSize = $this->size * 2;
148  $minX = Math::floorFloat($this->source->x - $explosionSize - 1);
149  $maxX = Math::floorFloat($this->source->x + $explosionSize + 1);
150  $minY = Math::floorFloat($this->source->y - $explosionSize - 1);
151  $maxY = Math::floorFloat($this->source->y + $explosionSize + 1);
152  $minZ = Math::floorFloat($this->source->z - $explosionSize - 1);
153  $maxZ = Math::floorFloat($this->source->z + $explosionSize + 1);
154 
155  $explosionBB = new AxisAlignedBB($minX, $minY, $minZ, $maxX, $maxY, $maxZ);
156 
157  $list = $this->level->getNearbyEntities($explosionBB, $this->what instanceof Entity ? $this->what : null);
158  foreach($list as $entity){
159  $distance = $entity->distance($this->source) / $explosionSize;
160 
161  if($distance <= 1){
162  $motion = $entity->subtract($this->source)->normalize();
163 
164  $impact = (1 - $distance) * ($exposure = 1);
165 
166  $damage = (int) ((($impact * $impact + $impact) / 2) * 8 * $explosionSize + 1);
167 
168  if($this->what instanceof Entity){
169  $ev = new EntityDamageByEntityEvent($this->what, $entity, EntityDamageEvent::CAUSE_ENTITY_EXPLOSION, $damage);
170  }elseif($this->what instanceof Block){
171  $ev = new EntityDamageByBlockEvent($this->what, $entity, EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, $damage);
172  }else{
173  $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, $damage);
174  }
175 
176  $entity->attack($ev->getFinalDamage(), $ev);
177  $entity->setMotion($motion->multiply($impact));
178  }
179  }
180 
181 
182  $air = Item::get(Item::AIR);
183 
184  foreach($this->affectedBlocks as $block){
185  if($block->getId() === Block::TNT){
186  $mot = (new Random())->nextSignedFloat() * M_PI * 2;
187  $tnt = Entity::createEntity("PrimedTNT", $this->level->getChunk($block->x >> 4, $block->z >> 4), new Compound("", [
188  "Pos" => new Enum("Pos", [
189  new Double("", $block->x + 0.5),
190  new Double("", $block->y),
191  new Double("", $block->z + 0.5)
192  ]),
193  "Motion" => new Enum("Motion", [
194  new Double("", -sin($mot) * 0.02),
195  new Double("", 0.2),
196  new Double("", -cos($mot) * 0.02)
197  ]),
198  "Rotation" => new Enum("Rotation", [
199  new Float("", 0),
200  new Float("", 0)
201  ]),
202  "Fuse" => new Byte("Fuse", mt_rand(10, 30))
203  ]));
204  $tnt->spawnToAll();
205  }elseif(mt_rand(0, 100) < $yield){
206  foreach($block->getDrops($air) as $drop){
207  $this->level->dropItem($block->add(0.5, 0.5, 0.5), Item::get(...$drop));
208  }
209  }
210  $this->level->setBlockIdAt($block->x, $block->y, $block->z, 0);
211  $send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z);
212  }
213  $pk = new ExplodePacket();
214  $pk->x = $this->source->x;
215  $pk->y = $this->source->y;
216  $pk->z = $this->source->z;
217  $pk->radius = $this->size;
218  $pk->records = $send;
219  Server::broadcastPacket($this->level->getUsingChunk($source->x >> 4, $source->z >> 4), $pk);
220 
221  return true;
222  }
223 }
static createEntity($type, FullChunk $chunk, Compound $nbt,...$args)
Definition: Entity.php:239
static broadcastPacket(array $players, DataPacket $packet)
Definition: Server.php:1725