39 private int $foodTickTimer = 0;
41 private bool $enabled =
true;
43 public function __construct(
46 $this->hungerAttr = self::fetchAttribute($entity, Attribute::HUNGER);
47 $this->saturationAttr = self::fetchAttribute($entity, Attribute::SATURATION);
48 $this->exhaustionAttr = self::fetchAttribute($entity, Attribute::EXHAUSTION);
51 private static function fetchAttribute(
Entity $entity,
string $attributeId) :
Attribute{
52 $attribute = AttributeFactory::getInstance()->mustGet($attributeId);
53 $entity->getAttributeMap()->add($attribute);
57 public function getFood() :
float{
58 return $this->hungerAttr->getValue();
67 public function setFood(
float $new) : void{
68 $old = $this->hungerAttr->getValue();
69 $this->hungerAttr->setValue($new);
72 foreach([17, 6, 0] as $bound){
73 if(($old > $bound) !== ($new > $bound)){
74 $this->foodTickTimer = 0;
80 public function getMaxFood() : float{
81 return $this->hungerAttr->getMaxValue();
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);
95 return $this->getFood() < $this->getMaxFood();
98 public function getSaturation() : float{
99 return $this->saturationAttr->getValue();
109 $this->saturationAttr->setValue($saturation);
112 public function addSaturation(
float $amount) : void{
113 $this->saturationAttr->setValue($this->saturationAttr->getValue() + $amount, true);
116 public function getExhaustion() : float{
117 return $this->exhaustionAttr->getValue();
125 $this->exhaustionAttr->setValue($exhaustion);
133 public function exhaust(
float $amount,
int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{
138 if(PlayerExhaustEvent::hasHandlers()){
141 if($ev->isCancelled()){
144 $evAmount = $ev->getAmount();
147 $exhaustion = $this->getExhaustion();
148 $exhaustion += $evAmount;
150 while($exhaustion >= 4.0){
153 $saturation = $this->getSaturation();
155 $saturation = max(0, $saturation - 1.0);
156 $this->setSaturation($saturation);
158 $food = $this->getFood();
161 $this->setFood(max($food, 0));
165 $this->setExhaustion($exhaustion);
170 public function getFoodTickTimer() : int{
171 return $this->foodTickTimer;
174 public function setFoodTickTimer(
int $foodTickTimer) : void{
175 if($foodTickTimer < 0){
176 throw new \InvalidArgumentException(
"Expected a non-negative value");
178 $this->foodTickTimer = $foodTickTimer;
181 public function tick(
int $tickDiff = 1) : void{
182 if(!$this->entity->isAlive() || !$this->enabled){
185 $food = $this->getFood();
186 $health = $this->entity->getHealth();
187 $difficulty = $this->entity->getWorld()->getDifficulty();
189 $this->foodTickTimer += $tickDiff;
190 if($this->foodTickTimer >= 80){
191 $this->foodTickTimer = 0;
194 if($difficulty === World::DIFFICULTY_PEACEFUL && $this->foodTickTimer % 10 === 0){
195 if($food < $this->getMaxFood()){
197 $food = $this->getFood();
199 if($this->foodTickTimer % 20 === 0 && $health < $this->entity->getMaxHealth()){
200 $this->entity->heal(
new EntityRegainHealthEvent($this->entity, 1, EntityRegainHealthEvent::CAUSE_SATURATION));
204 if($this->foodTickTimer === 0){
206 if($health < $this->entity->getMaxHealth()){
207 $this->entity->heal(
new EntityRegainHealthEvent($this->entity, 1, EntityRegainHealthEvent::CAUSE_SATURATION));
208 $this->exhaust(6.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN);
211 if(($difficulty === World::DIFFICULTY_EASY && $health > 10) || ($difficulty === World::DIFFICULTY_NORMAL && $health > 1) || $difficulty === World::DIFFICULTY_HARD){
212 $this->entity->attack(
new EntityDamageEvent($this->entity, EntityDamageEvent::CAUSE_STARVATION, 1));
218 $this->entity->setSprinting(
false);
222 public function isEnabled() : bool{
223 return $this->enabled;
226 public function setEnabled(
bool $enabled) : void{
227 $this->enabled = $enabled;