PocketMine-MP 5.33.2 git-1133d49c924b4358c79d44eeb97dcbf56cb4d1eb
Loading...
Searching...
No Matches
Campfire.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
22declare(strict_types=1);
23
24namespace pocketmine\block;
25
26use pocketmine\block\tile\Campfire as TileCampfire;
28use pocketmine\block\utils\HorizontalFacingOption;
29use pocketmine\block\utils\HorizontalFacingTrait;
31use pocketmine\block\utils\LightableTrait;
32use pocketmine\block\utils\SupportType;
34use pocketmine\crafting\FurnaceType;
61use function count;
62use function min;
63use function mt_rand;
64
66 use HorizontalFacingTrait{
67 HorizontalFacingTrait::describeBlockOnlyState as encodeFacingState;
68 }
69 use LightableTrait{
70 LightableTrait::describeBlockOnlyState as encodeLitState;
71 }
72
73 private const UPDATE_INTERVAL_TICKS = 10;
74
79 protected ?Inventory $inventory = null;
80
85 protected array $cookingTimes = [];
86
87 protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
88 $this->encodeFacingState($w);
89 $this->encodeLitState($w);
90 }
91
92 public function readStateFromWorld() : Block{
93 parent::readStateFromWorld();
94 $tile = $this->position->getWorld()->getTile($this->position);
95 if($tile instanceof TileCampfire){
96 $this->inventory = $tile->getInventory();
97 $this->cookingTimes = $tile->getCookingTimes();
98 }else{
99 $this->inventory = null;
100 $this->cookingTimes = [];
101 }
102
103 return $this;
104 }
105
106 public function writeStateToWorld() : void{
107 parent::writeStateToWorld();
108 $tile = $this->position->getWorld()->getTile($this->position);
109 if($tile instanceof TileCampfire){
110 $tile->setCookingTimes($this->cookingTimes);
111 }
112 }
113
114 public function hasEntityCollision() : bool{
115 return true;
116 }
117
118 public function getLightLevel() : int{
119 return $this->lit ? 15 : 0;
120 }
121
122 public function isAffectedBySilkTouch() : bool{
123 return true;
124 }
125
126 public function getDropsForCompatibleTool(Item $item) : array{
127 return [
128 VanillaItems::CHARCOAL()->setCount(2)
129 ];
130 }
131
132 public function getSupportType(Facing $facing) : SupportType{
133 return SupportType::NONE;
134 }
135
136 protected function recalculateCollisionBoxes() : array{
137 return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 9 / 16)];
138 }
139
144 public function getInventory() : ?Inventory{
145 return $this->inventory;
146 }
147
148 protected function getFurnaceType() : FurnaceType{
149 return FurnaceType::CAMPFIRE;
150 }
151
152 protected function getEntityCollisionDamage() : int{
153 return 1;
154 }
155
159 public function setCookingTime(int $slot, int $time) : void{
160 if($slot < 0 || $slot > 3){
161 throw new \InvalidArgumentException("Slot must be in range 0-3");
162 }
163 if($time < 0 || $time > $this->getFurnaceType()->getCookDurationTicks()){
164 throw new \InvalidArgumentException("CookingTime must be in range 0-" . $this->getFurnaceType()->getCookDurationTicks());
165 }
166 $this->cookingTimes[$slot] = $time;
167 }
168
172 public function getCookingTime(int $slot) : int{
173 return $this->cookingTimes[$slot] ?? 0;
174 }
175
176 public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{
177 if($this->getSide(Facing::DOWN) instanceof Campfire){
178 return false;
179 }
180 if($player !== null){
181 $this->facing = HorizontalFacingOption::fromFacing($player->getHorizontalFacing());
182 }
183 $this->lit = true;
184 return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
185 }
186
187 public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
188 if(!$this->lit){
189 if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){
190 $item->pop();
191 $this->ignite();
192 $this->position->getWorld()->addSound($this->position, new BlazeShootSound());
193 return true;
194 }elseif($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){
195 if($item instanceof Durable){
196 $item->applyDamage(1);
197 }
198 $this->ignite();
199 return true;
200 }
201 }elseif($item instanceof Shovel){
202 $item->applyDamage(1);
203 $this->extinguish();
204 return true;
205 }
206
207 $inventory = $this->inventory;
208 if($inventory !== null && $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($this->getFurnaceType())->match($item) !== null){
209 $ingredient = clone $item;
210 $ingredient->setCount(1);
211 if(count($inventory->addItem($ingredient)) === 0){
212 $item->pop();
213 $this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
214 return true;
215 }
216 }
217 return false;
218 }
219
220 public function onNearbyBlockChange() : void{
221 if($this->lit && $this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){
222 $this->extinguish();
223 //TODO: Waterlogging
224 }
225 }
226
227 public function onEntityInside(Entity $entity) : bool{
228 if(!$this->lit){
229 if($entity->isOnFire()){
230 $this->ignite();
231 return false;
232 }
233 }elseif($entity instanceof Living){
234 $entity->attack(new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, $this->getEntityCollisionDamage()));
235 }
236 return true;
237 }
238
239 public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
240 if($this->lit && $projectile instanceof SplashPotion && $projectile->getPotionType() === PotionType::WATER){
241 $this->extinguish();
242 }
243 }
244
245 public function onScheduledUpdate() : void{
246 if($this->lit && ($inventory = $this->inventory) !== null){
247 $items = $inventory->getContents();
248 $furnaceType = $this->getFurnaceType();
249 $maxCookDuration = $furnaceType->getCookDurationTicks();
250 foreach($items as $slot => $item){
251 $this->setCookingTime($slot, min($maxCookDuration, $this->getCookingTime($slot) + self::UPDATE_INTERVAL_TICKS));
252 if($this->getCookingTime($slot) >= $maxCookDuration){
253 $result =
254 ($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($item)) instanceof FurnaceRecipe ?
255 $recipe->getResult() :
256 VanillaItems::AIR();
257
258 $ev = new CampfireCookEvent($this, $slot, $item, $result);
259 $ev->call();
260
261 if ($ev->isCancelled()){
262 continue;
263 }
264
265 $inventory->setItem($slot, VanillaItems::AIR());
266 $this->setCookingTime($slot, 0);
267 $this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $ev->getResult());
268 }
269 }
270 if(count($items) > 0){
271 $this->position->getWorld()->setBlock($this->position, $this);
272 }
273 if(mt_rand(1, 6) === 1){
274 $this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound());
275 }
276 $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
277 }
278 }
279
280 private function extinguish() : void{
281 $this->position->getWorld()->addSound($this->position, new FireExtinguishSound());
282 $this->position->getWorld()->setBlock($this->position, $this->setLit(false));
283 }
284
285 private function ignite() : void{
286 $this->position->getWorld()->addSound($this->position, new FlintSteelSound());
287 $this->position->getWorld()->setBlock($this->position, $this->setLit(true));
288 $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS);
289 }
290}
onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player=null, array &$returnedItems=[])
Definition Campfire.php:187
describeBlockOnlyState(RuntimeDataDescriber $w)
Definition Campfire.php:87
onProjectileHit(Projectile $projectile, RayTraceResult $hitResult)
Definition Campfire.php:239
place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player=null)
Definition Campfire.php:176
setCookingTime(int $slot, int $time)
Definition Campfire.php:159
getDropsForCompatibleTool(Item $item)
Definition Campfire.php:126
onEntityInside(Entity $entity)
Definition Campfire.php:227
getSupportType(Facing $facing)
Definition Campfire.php:132
getContents(bool $includeEmpty=false)
setItem(int $index, Item $item)