PocketMine-MP 5.15.1 git-08c6e63aac45a450fe3b437616ee8aa2500e04e2
HungerManager.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\entity;
25
30use function max;
31use function min;
32
34
35 private Attribute $hungerAttr;
36 private Attribute $saturationAttr;
37 private Attribute $exhaustionAttr;
38
39 private int $foodTickTimer = 0;
40
41 private bool $enabled = true;
42
43 public function __construct(
44 private Human $entity
45 ){
46 $this->hungerAttr = self::fetchAttribute($entity, Attribute::HUNGER);
47 $this->saturationAttr = self::fetchAttribute($entity, Attribute::SATURATION);
48 $this->exhaustionAttr = self::fetchAttribute($entity, Attribute::EXHAUSTION);
49 }
50
51 private static function fetchAttribute(Entity $entity, string $attributeId) : Attribute{
52 $attribute = AttributeFactory::getInstance()->mustGet($attributeId);
53 $entity->getAttributeMap()->add($attribute);
54 return $attribute;
55 }
56
57 public function getFood() : float{
58 return $this->hungerAttr->getValue();
59 }
60
67 public function setFood(float $new) : void{
68 $old = $this->hungerAttr->getValue();
69 $this->hungerAttr->setValue($new);
70
71 // ranges: 18-20 (regen), 7-17 (none), 1-6 (no sprint), 0 (health depletion)
72 foreach([17, 6, 0] as $bound){
73 if(($old > $bound) !== ($new > $bound)){
74 $this->foodTickTimer = 0;
75 break;
76 }
77 }
78 }
79
80 public function getMaxFood() : float{
81 return $this->hungerAttr->getMaxValue();
82 }
83
84 public function addFood(float $amount) : void{
85 $amount += $this->hungerAttr->getValue();
86 $amount = max(min($amount, $this->hungerAttr->getMaxValue()), $this->hungerAttr->getMinValue());
87 $this->setFood($amount);
88 }
89
93 public function isHungry() : bool{
94 return $this->getFood() < $this->getMaxFood();
95 }
96
97 public function getSaturation() : float{
98 return $this->saturationAttr->getValue();
99 }
100
107 public function setSaturation(float $saturation) : void{
108 $this->saturationAttr->setValue($saturation);
109 }
110
111 public function addSaturation(float $amount) : void{
112 $this->saturationAttr->setValue($this->saturationAttr->getValue() + $amount, true);
113 }
114
115 public function getExhaustion() : float{
116 return $this->exhaustionAttr->getValue();
117 }
118
123 public function setExhaustion(float $exhaustion) : void{
124 $this->exhaustionAttr->setValue($exhaustion);
125 }
126
132 public function exhaust(float $amount, int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{
133 if(!$this->enabled){
134 return 0;
135 }
136 $evAmount = $amount;
137 if(PlayerExhaustEvent::hasHandlers()){
138 $ev = new PlayerExhaustEvent($this->entity, $amount, $cause);
139 $ev->call();
140 if($ev->isCancelled()){
141 return 0.0;
142 }
143 $evAmount = $ev->getAmount();
144 }
145
146 $exhaustion = $this->getExhaustion();
147 $exhaustion += $evAmount;
148
149 while($exhaustion >= 4.0){
150 $exhaustion -= 4.0;
151
152 $saturation = $this->getSaturation();
153 if($saturation > 0){
154 $saturation = max(0, $saturation - 1.0);
155 $this->setSaturation($saturation);
156 }else{
157 $food = $this->getFood();
158 if($food > 0){
159 $food--;
160 $this->setFood(max($food, 0));
161 }
162 }
163 }
164 $this->setExhaustion($exhaustion);
165
166 return $evAmount;
167 }
168
169 public function getFoodTickTimer() : int{
170 return $this->foodTickTimer;
171 }
172
173 public function setFoodTickTimer(int $foodTickTimer) : void{
174 if($foodTickTimer < 0){
175 throw new \InvalidArgumentException("Expected a non-negative value");
176 }
177 $this->foodTickTimer = $foodTickTimer;
178 }
179
180 public function tick(int $tickDiff = 1) : void{
181 if(!$this->entity->isAlive() || !$this->enabled){
182 return;
183 }
184 $food = $this->getFood();
185 $health = $this->entity->getHealth();
186 $difficulty = $this->entity->getWorld()->getDifficulty();
187
188 $this->foodTickTimer += $tickDiff;
189 if($this->foodTickTimer >= 80){
190 $this->foodTickTimer = 0;
191 }
192
193 if($difficulty === World::DIFFICULTY_PEACEFUL && $this->foodTickTimer % 10 === 0){
194 if($food < $this->getMaxFood()){
195 $this->addFood(1.0);
196 $food = $this->getFood();
197 }
198 if($this->foodTickTimer % 20 === 0 && $health < $this->entity->getMaxHealth()){
199 $this->entity->heal(new EntityRegainHealthEvent($this->entity, 1, EntityRegainHealthEvent::CAUSE_SATURATION));
200 }
201 }
202
203 if($this->foodTickTimer === 0){
204 if($food >= 18){
205 if($health < $this->entity->getMaxHealth()){
206 $this->entity->heal(new EntityRegainHealthEvent($this->entity, 1, EntityRegainHealthEvent::CAUSE_SATURATION));
207 $this->exhaust(6.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN);
208 }
209 }elseif($food <= 0){
210 if(($difficulty === World::DIFFICULTY_EASY && $health > 10) || ($difficulty === World::DIFFICULTY_NORMAL && $health > 1) || $difficulty === World::DIFFICULTY_HARD){
211 $this->entity->attack(new EntityDamageEvent($this->entity, EntityDamageEvent::CAUSE_STARVATION, 1));
212 }
213 }
214 }
215
216 if($food <= 6){
217 $this->entity->setSprinting(false);
218 }
219 }
220
221 public function isEnabled() : bool{
222 return $this->enabled;
223 }
224
225 public function setEnabled(bool $enabled) : void{
226 $this->enabled = $enabled;
227 }
228}
setExhaustion(float $exhaustion)
setSaturation(float $saturation)
exhaust(float $amount, int $cause=PlayerExhaustEvent::CAUSE_CUSTOM)