the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "JavaMath.h"
3#include "Mth.h"
4#include "net.minecraft.network.packet.h"
5#include "net.minecraft.world.level.h"
6#include "net.minecraft.world.level.tile.h"
7#include "net.minecraft.world.phys.h"
8#include "net.minecraft.world.entity.h"
9#include "net.minecraft.world.entity.ai.attributes.h"
10#include "net.minecraft.world.entity.ai.control.h"
11#include "net.minecraft.world.entity.ai.navigation.h"
12#include "net.minecraft.world.entity.ai.sensing.h"
13#include "net.minecraft.world.entity.player.h"
14#include "net.minecraft.world.entity.animal.h"
15#include "net.minecraft.world.entity.monster.h"
16#include "net.minecraft.world.item.h"
17#include "net.minecraft.world.level.h"
18#include "net.minecraft.world.level.chunk.h"
19#include "net.minecraft.world.level.material.h"
20#include "net.minecraft.world.damagesource.h"
21#include "net.minecraft.world.effect.h"
22#include "net.minecraft.world.item.alchemy.h"
23#include "net.minecraft.world.item.enchantment.h"
24#include "net.minecraft.world.scores.h"
25#include "com.mojang.nbt.h"
26#include "LivingEntity.h"
27#include "..\Minecraft.Client\Textures.h"
28#include "..\Minecraft.Client\ServerLevel.h"
29#include "..\Minecraft.Client\EntityTracker.h"
30#include "SoundTypes.h"
31#include "BasicTypeContainers.h"
32#include "ParticleTypes.h"
33#include "GenericStats.h"
34#include "ItemEntity.h"
35
36const double LivingEntity::MIN_MOVEMENT_DISTANCE = 0.005;
37
38AttributeModifier *LivingEntity::SPEED_MODIFIER_SPRINTING = (new AttributeModifier(eModifierId_MOB_SPRINTING, 0.3f, AttributeModifier::OPERATION_MULTIPLY_TOTAL))->setSerialize(false);
39
40void LivingEntity::_init()
41{
42 attributes = NULL;
43 combatTracker = new CombatTracker(this);
44 lastEquipment = ItemInstanceArray(5);
45
46 swinging = false;
47 swingTime = 0;
48 removeArrowTime = 0;
49 lastHealth = 0.0f;
50
51 hurtTime = 0;
52 hurtDuration = 0;
53 hurtDir = 0.0f;
54 deathTime = 0;
55 attackTime = 0;
56 oAttackAnim = attackAnim = 0.0f;
57
58 walkAnimSpeedO = 0.0f;
59 walkAnimSpeed = 0.0f;
60 walkAnimPos = 0.0f;
61 invulnerableDuration = 20;
62 oTilt = tilt = 0.0f;
63 timeOffs = 0.0f;
64 rotA = 0.0f;
65 yBodyRot = yBodyRotO = 0.0f;
66 yHeadRot = yHeadRotO = 0.0f;
67 flyingSpeed = 0.02f;
68
69 lastHurtByPlayer = nullptr;
70 lastHurtByPlayerTime = 0;
71 dead = false;
72 noActionTime = 0;
73 oRun = run = 0.0f;
74 animStep = animStepO = 0.0f;
75 rotOffs = 0.0f;
76 deathScore = 0;
77 lastHurt = 0.0f;
78 jumping = false;
79
80 xxa = 0.0f;
81 yya = 0.0f;
82 yRotA = 0.0f;
83 lSteps = 0;
84 lx = ly = lz = lyr = lxr = 0.0;
85
86 effectsDirty = false;
87
88 lastHurtByMob = nullptr;
89 lastHurtByMobTimestamp = 0;
90 lastHurtMob = nullptr;
91 lastHurtMobTimestamp = 0;
92
93 speed = 0.0f;
94 noJumpDelay = 0;
95 absorptionAmount = 0.0f;
96}
97
98LivingEntity::LivingEntity( Level* level) : Entity(level)
99{
100 MemSect(56);
101 _init();
102 MemSect(0);
103
104 // 4J Stu - This will not call the correct derived function, so moving to each derived class
105 //setHealth(0);
106 //registerAttributes();
107
108 blocksBuilding = true;
109
110 rotA = (float) (Math::random() + 1) * 0.01f;
111 setPos(x, y, z);
112 timeOffs = (float) Math::random() * 12398;
113 yRot = (float) (Math::random() * PI * 2);
114 yHeadRot = yRot;
115
116 footSize = 0.5f;
117}
118
119LivingEntity::~LivingEntity()
120{
121 for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it)
122 {
123 delete it->second;
124 }
125
126 delete attributes;
127 delete combatTracker;
128
129 if(lastEquipment.data != NULL) delete [] lastEquipment.data;
130}
131
132void LivingEntity::defineSynchedData()
133{
134 entityData->define(DATA_EFFECT_COLOR_ID, 0);
135 entityData->define(DATA_EFFECT_AMBIENCE_ID, (byte) 0);
136 entityData->define(DATA_ARROW_COUNT_ID, (byte) 0);
137 entityData->define(DATA_HEALTH_ID, 1.0f);
138}
139
140void LivingEntity::registerAttributes()
141{
142 getAttributes()->registerAttribute(SharedMonsterAttributes::MAX_HEALTH);
143 getAttributes()->registerAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE);
144 getAttributes()->registerAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
145
146 if (!useNewAi())
147 {
148 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.1f);
149 }
150}
151
152void LivingEntity::checkFallDamage(double ya, bool onGround)
153{
154 if (!isInWater())
155 {
156 // double-check if we've reached water in this move tick
157 updateInWaterState();
158 }
159
160 if (onGround && fallDistance > 0)
161 {
162 int xt = Mth::floor(x);
163 int yt = Mth::floor(y - 0.2f - heightOffset);
164 int zt = Mth::floor(z);
165 int t = level->getTile(xt, yt, zt);
166 if (t == 0)
167 {
168 int renderShape = level->getTileRenderShape(xt, yt - 1, zt);
169 if (renderShape == Tile::SHAPE_FENCE || renderShape == Tile::SHAPE_WALL || renderShape == Tile::SHAPE_FENCE_GATE)
170 {
171 t = level->getTile(xt, yt - 1, zt);
172 }
173 }
174
175 if (t > 0)
176 {
177 Tile::tiles[t]->fallOn(level, xt, yt, zt, shared_from_this(), fallDistance);
178 }
179 }
180
181 Entity::checkFallDamage(ya, onGround);
182}
183
184bool LivingEntity::isWaterMob()
185{
186 return false;
187}
188
189void LivingEntity::baseTick()
190{
191 oAttackAnim = attackAnim;
192 Entity::baseTick();
193
194 if (isAlive() && isInWall())
195 {
196 hurt(DamageSource::inWall, 1);
197 }
198
199 if (isFireImmune() || level->isClientSide) clearFire();
200 shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
201 bool isInvulnerable = (thisPlayer != NULL && thisPlayer->abilities.invulnerable);
202
203 if (isAlive() && isUnderLiquid(Material::water))
204 {
205 if(!isWaterMob() && !hasEffect(MobEffect::waterBreathing->id) && !isInvulnerable)
206 {
207 setAirSupply(decreaseAirSupply(getAirSupply()));
208 if (getAirSupply() == -20)
209 {
210 setAirSupply(0);
211 if(canCreateParticles())
212 {
213 for (int i = 0; i < 8; i++)
214 {
215 float xo = random->nextFloat() - random->nextFloat();
216 float yo = random->nextFloat() - random->nextFloat();
217 float zo = random->nextFloat() - random->nextFloat();
218 level->addParticle(eParticleType_bubble, x + xo, y + yo, z + zo, xd, yd, zd);
219 }
220 }
221 hurt(DamageSource::drown, 2);
222 }
223 }
224
225 clearFire();
226 if ( !level->isClientSide && isRiding() && riding->instanceof(eTYPE_LIVINGENTITY) )
227 {
228 ride(nullptr);
229 }
230 }
231 else
232 {
233 setAirSupply(TOTAL_AIR_SUPPLY);
234 }
235
236 oTilt = tilt;
237
238 if (attackTime > 0) attackTime--;
239 if (hurtTime > 0) hurtTime--;
240 if (invulnerableTime > 0) invulnerableTime--;
241 if (getHealth() <= 0)
242 {
243 tickDeath();
244 }
245
246 if (lastHurtByPlayerTime > 0) lastHurtByPlayerTime--;
247 else
248 {
249 // Note - this used to just set to nullptr, but that has to create a new shared_ptr and free an old one, when generally this won't be doing anything at all. This
250 // is the lightweight but ugly alternative
251 if( lastHurtByPlayer )
252 {
253 lastHurtByPlayer.reset();
254 }
255 }
256 if (lastHurtMob != NULL && !lastHurtMob->isAlive())
257 {
258 lastHurtMob = nullptr;
259 }
260
261 // If lastHurtByMob is dead, remove it
262 if (lastHurtByMob != NULL && !lastHurtByMob->isAlive())
263 {
264 setLastHurtByMob(nullptr);
265 }
266
267 // Update effects
268 tickEffects();
269
270 animStepO = animStep;
271
272 yBodyRotO = yBodyRot;
273 yHeadRotO = yHeadRot;
274 yRotO = yRot;
275 xRotO = xRot;
276}
277
278bool LivingEntity::isBaby()
279{
280 return false;
281}
282
283void LivingEntity::tickDeath()
284{
285 deathTime++;
286 if (deathTime == 20)
287 {
288 // 4J Stu - Added level->isClientSide check from 1.2 to fix XP orbs being created client side
289 if(!level->isClientSide && (lastHurtByPlayerTime > 0 || isAlwaysExperienceDropper()) )
290 {
291 if (!isBaby() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT))
292 {
293 int xpCount = this->getExperienceReward(lastHurtByPlayer);
294 while (xpCount > 0)
295 {
296 int newCount = ExperienceOrb::getExperienceValue(xpCount);
297 xpCount -= newCount;
298 level->addEntity(shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, newCount) ) );
299 }
300 }
301 }
302
303 remove();
304 for (int i = 0; i < 20; i++)
305 {
306 double xa = random->nextGaussian() * 0.02;
307 double ya = random->nextGaussian() * 0.02;
308 double za = random->nextGaussian() * 0.02;
309 level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
310 }
311 }
312}
313
314int LivingEntity::decreaseAirSupply(int currentSupply)
315{
316 int oxygenBonus = EnchantmentHelper::getOxygenBonus(dynamic_pointer_cast<LivingEntity>(shared_from_this()));
317 if (oxygenBonus > 0)
318 {
319 if (random->nextInt(oxygenBonus + 1) > 0)
320 {
321 // the oxygen bonus prevents us from drowning
322 return currentSupply;
323 }
324 }
325 if(instanceof(eTYPE_PLAYER))
326 {
327 app.DebugPrintf("++++++++++ %s: Player decreasing air supply to %d\n", level->isClientSide ? "CLIENT" : "SERVER", currentSupply - 1 );
328 }
329 return currentSupply - 1;
330}
331
332int LivingEntity::getExperienceReward(shared_ptr<Player> killedBy)
333{
334 return 0;
335}
336
337bool LivingEntity::isAlwaysExperienceDropper()
338{
339 return false;
340}
341
342Random *LivingEntity::getRandom()
343{
344 return random;
345}
346
347shared_ptr<LivingEntity> LivingEntity::getLastHurtByMob()
348{
349 return lastHurtByMob;
350}
351
352int LivingEntity::getLastHurtByMobTimestamp()
353{
354 return lastHurtByMobTimestamp;
355}
356
357void LivingEntity::setLastHurtByMob(shared_ptr<LivingEntity> target)
358{
359 lastHurtByMob = target;
360 lastHurtByMobTimestamp = tickCount;
361}
362
363shared_ptr<LivingEntity> LivingEntity::getLastHurtMob()
364{
365 return lastHurtMob;
366}
367
368int LivingEntity::getLastHurtMobTimestamp()
369{
370 return lastHurtMobTimestamp;
371}
372
373void LivingEntity::setLastHurtMob(shared_ptr<Entity> target)
374{
375 if ( target->instanceof(eTYPE_LIVINGENTITY) )
376 {
377 lastHurtMob = dynamic_pointer_cast<LivingEntity>(target);
378 }
379 else
380 {
381 lastHurtMob = nullptr;
382 }
383 lastHurtMobTimestamp = tickCount;
384}
385
386int LivingEntity::getNoActionTime()
387{
388 return noActionTime;
389}
390
391void LivingEntity::addAdditonalSaveData(CompoundTag *entityTag)
392{
393 entityTag->putFloat(L"HealF", getHealth());
394 entityTag->putShort(L"Health", (short) ceil(getHealth()));
395 entityTag->putShort(L"HurtTime", (short) hurtTime);
396 entityTag->putShort(L"DeathTime", (short) deathTime);
397 entityTag->putShort(L"AttackTime", (short) attackTime);
398 entityTag->putFloat(L"AbsorptionAmount", getAbsorptionAmount());
399
400 ItemInstanceArray items = getEquipmentSlots();
401 for (unsigned int i = 0; i < items.length; ++i)
402 {
403 shared_ptr<ItemInstance> item = items[i];
404 if (item != NULL)
405 {
406 attributes->removeItemModifiers(item);
407 }
408 }
409
410 entityTag->put(L"Attributes", SharedMonsterAttributes::saveAttributes(getAttributes()));
411
412 for (unsigned int i = 0; i < items.length; ++i)
413 {
414 shared_ptr<ItemInstance> item = items[i];
415 if (item != NULL)
416 {
417 attributes->addItemModifiers(item);
418 }
419 }
420
421 if (!activeEffects.empty())
422 {
423 ListTag<CompoundTag> *listTag = new ListTag<CompoundTag>();
424
425 for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it)
426 {
427 MobEffectInstance *effect = it->second;
428 listTag->add(effect->save(new CompoundTag()));
429 }
430 entityTag->put(L"ActiveEffects", listTag);
431 }
432}
433
434void LivingEntity::readAdditionalSaveData(CompoundTag *tag)
435{
436 setAbsorptionAmount(tag->getFloat(L"AbsorptionAmount"));
437
438 if (tag->contains(L"Attributes") && level != NULL && !level->isClientSide)
439 {
440 SharedMonsterAttributes::loadAttributes(getAttributes(), (ListTag<CompoundTag> *) tag->getList(L"Attributes"));
441 }
442
443 if (tag->contains(L"ActiveEffects"))
444 {
445 ListTag<CompoundTag> *effects = (ListTag<CompoundTag> *) tag->getList(L"ActiveEffects");
446 for (int i = 0; i < effects->size(); i++)
447 {
448 CompoundTag *effectTag = effects->get(i);
449 MobEffectInstance *effect = MobEffectInstance::load(effectTag);
450 activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( effect->getId(), effect ) );
451 }
452 }
453
454 if (tag->contains(L"HealF"))
455 {
456 setHealth( tag->getFloat(L"HealF") );
457 }
458 else
459 {
460 Tag *healthTag = tag->get(L"Health");
461 if (healthTag == NULL)
462 {
463 setHealth(getMaxHealth());
464 }
465 else if (healthTag->getId() == Tag::TAG_Float)
466 {
467 setHealth(((FloatTag *) healthTag)->data);
468 }
469 else if (healthTag->getId() == Tag::TAG_Short)
470 {
471 // pre-1.6 health
472 setHealth((float) ((ShortTag *) healthTag)->data);
473 }
474 }
475
476 hurtTime = tag->getShort(L"HurtTime");
477 deathTime = tag->getShort(L"DeathTime");
478 attackTime = tag->getShort(L"AttackTime");
479}
480
481void LivingEntity::tickEffects()
482{
483 bool removed = false;
484 for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();)
485 {
486 MobEffectInstance *effect = it->second;
487 removed = false;
488 if (!effect->tick(dynamic_pointer_cast<LivingEntity>(shared_from_this())))
489 {
490 if (!level->isClientSide)
491 {
492 it = activeEffects.erase( it );
493 onEffectRemoved(effect);
494 delete effect;
495 removed = true;
496 }
497 }
498 else if (effect->getDuration() % (SharedConstants::TICKS_PER_SECOND * 30) == 0)
499 {
500 // update effects every 30 seconds to synchronize client-side
501 // timer
502 onEffectUpdated(effect, false);
503 }
504 if(!removed)
505 {
506 ++it;
507 }
508 }
509 if (effectsDirty)
510 {
511 if (!level->isClientSide)
512 {
513 if (activeEffects.empty())
514 {
515 entityData->set(DATA_EFFECT_AMBIENCE_ID, (byte) 0);
516 entityData->set(DATA_EFFECT_COLOR_ID, 0);
517 setInvisible(false);
518 setWeakened(false);
519 }
520 else
521 {
522 vector<MobEffectInstance *> values;
523 for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();++it)
524 {
525 values.push_back(it->second);
526 }
527 int colorValue = PotionBrewing::getColorValue(&values);
528 entityData->set(DATA_EFFECT_AMBIENCE_ID, PotionBrewing::areAllEffectsAmbient(&values) ? (byte) 1 : (byte) 0);
529 values.clear();
530 entityData->set(DATA_EFFECT_COLOR_ID, colorValue);
531 setInvisible(hasEffect(MobEffect::invisibility->id));
532 setWeakened(hasEffect(MobEffect::weakness->id));
533 }
534 }
535 effectsDirty = false;
536 }
537 int colorValue = entityData->getInteger(DATA_EFFECT_COLOR_ID);
538 bool ambient = entityData->getByte(DATA_EFFECT_AMBIENCE_ID) > 0;
539
540 if (colorValue > 0)
541 {
542 boolean doParticle = false;
543
544 if (!isInvisible())
545 {
546 doParticle = random->nextBoolean();
547 }
548 else
549 {
550 // much fewer particles when invisible
551 doParticle = random->nextInt(15) == 0;
552 }
553
554 if (ambient) doParticle &= random->nextInt(5) == 0;
555
556 if (doParticle)
557 {
558 // int colorValue = entityData.getInteger(DATA_EFFECT_COLOR_ID);
559 if (colorValue > 0)
560 {
561 double red = (double) ((colorValue >> 16) & 0xff) / 255.0;
562 double green = (double) ((colorValue >> 8) & 0xff) / 255.0;
563 double blue = (double) ((colorValue >> 0) & 0xff) / 255.0;
564
565 level->addParticle(ambient? eParticleType_mobSpellAmbient : eParticleType_mobSpell, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - heightOffset, z + (random->nextDouble() - 0.5) * bbWidth, red, green, blue);
566 }
567 }
568 }
569}
570
571void LivingEntity::removeAllEffects()
572{
573 //Iterator<Integer> effectIdIterator = activeEffects.keySet().iterator();
574 //while (effectIdIterator.hasNext())
575 for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); )
576 {
577 //Integer effectId = effectIdIterator.next();
578 MobEffectInstance *effect = it->second;//activeEffects.get(effectId);
579
580 if (!level->isClientSide)
581 {
582 //effectIdIterator.remove();
583 it = activeEffects.erase(it);
584 onEffectRemoved(effect);
585 delete effect;
586 }
587 else
588 {
589 ++it;
590 }
591 }
592}
593
594vector<MobEffectInstance *> *LivingEntity::getActiveEffects()
595{
596 vector<MobEffectInstance *> *active = new vector<MobEffectInstance *>();
597
598 for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it)
599 {
600 active->push_back(it->second);
601 }
602
603 return active;
604}
605
606bool LivingEntity::hasEffect(int id)
607{
608 return activeEffects.find(id) != activeEffects.end();;
609}
610
611bool LivingEntity::hasEffect(MobEffect *effect)
612{
613 return activeEffects.find(effect->id) != activeEffects.end();
614}
615
616MobEffectInstance *LivingEntity::getEffect(MobEffect *effect)
617{
618 MobEffectInstance *effectInst = NULL;
619
620 AUTO_VAR(it, activeEffects.find(effect->id));
621 if(it != activeEffects.end() ) effectInst = it->second;
622
623 return effectInst;
624}
625
626void LivingEntity::addEffect(MobEffectInstance *newEffect)
627{
628 if (!canBeAffected(newEffect))
629 {
630 return;
631 }
632
633 if (activeEffects.find(newEffect->getId()) != activeEffects.end() )
634 {
635 // replace effect and update
636 MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second;
637 effectInst->update(newEffect);
638 onEffectUpdated(effectInst, true);
639 }
640 else
641 {
642 activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( newEffect->getId(), newEffect ) );
643 onEffectAdded(newEffect);
644 }
645}
646
647// 4J Added
648void LivingEntity::addEffectNoUpdate(MobEffectInstance *newEffect)
649{
650 if (!canBeAffected(newEffect))
651 {
652 return;
653 }
654
655 if (activeEffects.find(newEffect->getId()) != activeEffects.end() )
656 {
657 // replace effect and update
658 MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second;
659 effectInst->update(newEffect);
660 }
661 else
662 {
663 activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( newEffect->getId(), newEffect ) );
664 }
665}
666
667bool LivingEntity::canBeAffected(MobEffectInstance *newEffect)
668{
669 if (getMobType() == UNDEAD)
670 {
671 int id = newEffect->getId();
672 if (id == MobEffect::regeneration->id || id == MobEffect::poison->id)
673 {
674 return false;
675 }
676 }
677
678 return true;
679}
680
681bool LivingEntity::isInvertedHealAndHarm()
682{
683 return getMobType() == UNDEAD;
684}
685
686void LivingEntity::removeEffectNoUpdate(int effectId)
687{
688 AUTO_VAR(it, activeEffects.find(effectId));
689 if (it != activeEffects.end())
690 {
691 MobEffectInstance *effect = it->second;
692 if(effect != NULL)
693 {
694 delete effect;
695 }
696 activeEffects.erase(it);
697 }
698}
699
700void LivingEntity::removeEffect(int effectId)
701{
702 AUTO_VAR(it, activeEffects.find(effectId));
703 if (it != activeEffects.end())
704 {
705 MobEffectInstance *effect = it->second;
706 if(effect != NULL)
707 {
708 onEffectRemoved(effect);
709 delete effect;
710 }
711 activeEffects.erase(it);
712 }
713}
714
715void LivingEntity::onEffectAdded(MobEffectInstance *effect)
716{
717 effectsDirty = true;
718 if (!level->isClientSide) MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier());
719}
720
721void LivingEntity::onEffectUpdated(MobEffectInstance *effect, bool doRefreshAttributes)
722{
723 effectsDirty = true;
724 if (doRefreshAttributes && !level->isClientSide)
725 {
726 MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier());
727 MobEffect::effects[effect->getId()]->addAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier());
728 }
729}
730
731void LivingEntity::onEffectRemoved(MobEffectInstance *effect)
732{
733 effectsDirty = true;
734 if (!level->isClientSide) MobEffect::effects[effect->getId()]->removeAttributeModifiers(dynamic_pointer_cast<LivingEntity>(shared_from_this()), getAttributes(), effect->getAmplifier());
735}
736
737void LivingEntity::heal(float heal)
738{
739 float health = getHealth();
740 if (health > 0)
741 {
742 setHealth(health + heal);
743 }
744}
745
746float LivingEntity::getHealth()
747{
748 return entityData->getFloat(DATA_HEALTH_ID);
749}
750
751void LivingEntity::setHealth(float health)
752{
753 entityData->set(DATA_HEALTH_ID, Mth::clamp(health, 0.0f, getMaxHealth()));
754}
755
756bool LivingEntity::hurt(DamageSource *source, float dmg)
757{
758 if (isInvulnerable()) return false;
759
760 // 4J Stu - Reworked this function a bit to show hurt damage on the client before the server responds.
761 // Fix for #8823 - Gameplay: Confirmation that a monster or animal has taken damage from an attack is highly delayed
762 // 4J Stu - Change to the fix to only show damage when attacked, rather than collision damage
763 // Fix for #10299 - When in corners, passive mobs may show that they are taking damage.
764 // 4J Stu - Change to the fix for TU6, as source is never NULL due to changes in 1.8.2 to what source actually is
765 if (level->isClientSide && dynamic_cast<EntityDamageSource *>(source) == NULL) return false;
766 noActionTime = 0;
767 if (getHealth() <= 0) return false;
768
769 if ( source->isFire() && hasEffect(MobEffect::fireResistance) )
770 {
771 // 4J-JEV, for new achievement Stayin'Frosty, TODO merge with Java version.
772 if ( this->instanceof(eTYPE_PLAYER) && (source == DamageSource::lava) ) // Only award when in lava (not any fire).
773 {
774 shared_ptr<Player> plr = dynamic_pointer_cast<Player>(shared_from_this());
775 plr->awardStat(GenericStats::stayinFrosty(),GenericStats::param_stayinFrosty());
776 }
777 return false;
778 }
779
780 if ((source == DamageSource::anvil || source == DamageSource::fallingBlock) && getCarried(SLOT_HELM) != NULL)
781 {
782 getCarried(SLOT_HELM)->hurtAndBreak((int) (dmg * 4 + random->nextFloat() * dmg * 2.0f), dynamic_pointer_cast<LivingEntity>( shared_from_this() ));
783 dmg *= 0.75f;
784 }
785
786 walkAnimSpeed = 1.5f;
787
788 bool sound = true;
789 if (invulnerableTime > invulnerableDuration / 2.0f)
790 {
791 if (dmg <= lastHurt) return false;
792 if(!level->isClientSide) actuallyHurt(source, dmg - lastHurt);
793 lastHurt = dmg;
794 sound = false;
795 }
796 else
797 {
798 lastHurt = dmg;
799 lastHealth = getHealth();
800 invulnerableTime = invulnerableDuration;
801 if (!level->isClientSide) actuallyHurt(source, dmg);
802 hurtTime = hurtDuration = 10;
803 }
804
805 hurtDir = 0;
806
807 shared_ptr<Entity> sourceEntity = source->getEntity();
808 if (sourceEntity != NULL)
809 {
810 if ( sourceEntity->instanceof(eTYPE_LIVINGENTITY) )
811 {
812 setLastHurtByMob(dynamic_pointer_cast<LivingEntity>(sourceEntity));
813 }
814
815 if ( sourceEntity->instanceof(eTYPE_PLAYER) )
816 {
817 lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME;
818 lastHurtByPlayer = dynamic_pointer_cast<Player>(sourceEntity);
819 }
820 else if ( sourceEntity->instanceof(eTYPE_WOLF) )
821 {
822 shared_ptr<Wolf> w = dynamic_pointer_cast<Wolf>(sourceEntity);
823 if (w->isTame())
824 {
825 lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME;
826 lastHurtByPlayer = nullptr;
827 }
828 }
829 }
830
831 if (sound && level->isClientSide)
832 {
833 return false;
834 }
835
836 if (sound)
837 {
838 level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT);
839 if (source != DamageSource::drown) markHurt();
840 if (sourceEntity != NULL)
841 {
842 double xd = sourceEntity->x - x;
843 double zd = sourceEntity->z - z;
844 while (xd * xd + zd * zd < 0.0001)
845 {
846 xd = (Math::random() - Math::random()) * 0.01;
847 zd = (Math::random() - Math::random()) * 0.01;
848 }
849 hurtDir = (float) (atan2(zd, xd) * 180 / PI) - yRot;
850 knockback(sourceEntity, dmg, xd, zd);
851 }
852 else
853 {
854 hurtDir = (float) (int) ((Math::random() * 2) * 180); // 4J This cast is the same as Java
855 }
856 }
857
858 MemSect(31);
859 if (getHealth() <= 0)
860 {
861 if (sound) playSound(getDeathSound(), getSoundVolume(), getVoicePitch());
862 die(source);
863 }
864 else
865 {
866 if (sound) playSound(getHurtSound(), getSoundVolume(), getVoicePitch());
867 }
868 MemSect(0);
869
870 return true;
871}
872
873void LivingEntity::breakItem(shared_ptr<ItemInstance> itemInstance)
874{
875 playSound(eSoundType_RANDOM_BREAK, 0.8f, 0.8f + level->random->nextFloat() * 0.4f);
876
877 for (int i = 0; i < 5; i++)
878 {
879 Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0);
880 d->xRot(-xRot * PI / 180);
881 d->yRot(-yRot * PI / 180);
882
883 Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6);
884 p->xRot(-xRot * PI / 180);
885 p->yRot(-yRot * PI / 180);
886 p = p->add(x, y + getHeadHeight(), z);
887 level->addParticle(PARTICLE_ICONCRACK(itemInstance->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z);
888 }
889}
890
891void LivingEntity::die(DamageSource *source)
892{
893 shared_ptr<Entity> sourceEntity = source->getEntity();
894 shared_ptr<LivingEntity> killer = getKillCredit();
895 if (deathScore >= 0 && killer != NULL) killer->awardKillScore(shared_from_this(), deathScore);
896
897 if (sourceEntity != NULL) sourceEntity->killed( dynamic_pointer_cast<LivingEntity>( shared_from_this() ) );
898
899 dead = true;
900
901 if (!level->isClientSide)
902 {
903 int playerBonus = 0;
904
905 shared_ptr<Player> player = nullptr;
906 if ( (sourceEntity != NULL) && sourceEntity->instanceof(eTYPE_PLAYER) )
907 {
908 player = dynamic_pointer_cast<Player>(sourceEntity);
909 playerBonus = EnchantmentHelper::getKillingLootBonus(dynamic_pointer_cast<LivingEntity>(player));
910 }
911
912 if (!isBaby() && level->getGameRules()->getBoolean(GameRules::RULE_DOMOBLOOT))
913 {
914 dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus);
915 dropEquipment(lastHurtByPlayerTime > 0, playerBonus);
916 if (lastHurtByPlayerTime > 0)
917 {
918 int rareLoot = random->nextInt(200) - playerBonus;
919 if (rareLoot < 5)
920 {
921 dropRareDeathLoot((rareLoot <= 0) ? 1 : 0);
922 }
923 }
924 }
925
926 // 4J-JEV, hook for Durango mobKill event.
927 if (player != NULL)
928 {
929 player->awardStat(GenericStats::killMob(),GenericStats::param_mobKill(player, dynamic_pointer_cast<Mob>(shared_from_this()), source));
930 }
931 }
932
933 level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH);
934}
935
936void LivingEntity::dropEquipment(bool byPlayer, int playerBonusLevel)
937{
938}
939
940void LivingEntity::knockback(shared_ptr<Entity> source, float dmg, double xd, double zd)
941{
942 if (random->nextDouble() < getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue())
943 {
944 return;
945 }
946
947 hasImpulse = true;
948 float dd = Mth::sqrt(xd * xd + zd * zd);
949 float pow = 0.4f;
950
951 this->xd /= 2;
952 yd /= 2;
953 this->zd /= 2;
954
955 this->xd -= xd / dd * pow;
956 yd += pow;
957 this->zd -= zd / dd * pow;
958
959 if (yd > 0.4f) yd = 0.4f;
960}
961
962int LivingEntity::getHurtSound()
963{
964 return eSoundType_DAMAGE_HURT;
965}
966
967int LivingEntity::getDeathSound()
968{
969 return eSoundType_DAMAGE_HURT;
970}
971
972/**
973* Drop extra rare loot. Only occurs roughly 5% of the time, rareRootLevel
974* is set to 1 (otherwise 0) 1% of the time.
975*
976* @param rareLootLevel
977*/
978void LivingEntity::dropRareDeathLoot(int rareLootLevel)
979{
980
981}
982
983void LivingEntity::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel)
984{
985}
986
987bool LivingEntity::onLadder()
988{
989 int xt = Mth::floor(x);
990 int yt = Mth::floor(bb->y0);
991 int zt = Mth::floor(z);
992
993 // 4J-PB - TU9 - add climbable vines
994 int iTile = level->getTile(xt, yt, zt);
995 return (iTile== Tile::ladder_Id) || (iTile== Tile::vine_Id);
996}
997
998bool LivingEntity::isShootable()
999{
1000 return true;
1001}
1002
1003bool LivingEntity::isAlive()
1004{
1005 return !removed && getHealth() > 0;
1006}
1007
1008void LivingEntity::causeFallDamage(float distance)
1009{
1010 Entity::causeFallDamage(distance);
1011 MobEffectInstance *jumpBoost = getEffect(MobEffect::jump);
1012 float padding = jumpBoost != NULL ? jumpBoost->getAmplifier() + 1 : 0;
1013
1014 int dmg = (int) ceil(distance - 3 - padding);
1015 if (dmg > 0)
1016 {
1017 // 4J - new sounds here brought forward from 1.2.3
1018 if (dmg > 4)
1019 {
1020 playSound(eSoundType_DAMAGE_FALL_BIG, 1, 1);
1021 }
1022 else
1023 {
1024 playSound(eSoundType_DAMAGE_FALL_SMALL, 1, 1);
1025 }
1026 hurt(DamageSource::fall, dmg);
1027
1028 int t = level->getTile( Mth::floor(x), Mth::floor(y - 0.2f - this->heightOffset), Mth::floor(z));
1029 if (t > 0)
1030 {
1031 const Tile::SoundType *soundType = Tile::tiles[t]->soundType;
1032 MemSect(31);
1033 playSound(soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f);
1034 MemSect(0);
1035 }
1036 }
1037}
1038
1039void LivingEntity::animateHurt()
1040{
1041 hurtTime = hurtDuration = 10;
1042 hurtDir = 0;
1043}
1044
1045/**
1046* Fetches the mob's armor value, from 0 (no armor) to 20 (full armor)
1047*
1048* @return
1049*/
1050int LivingEntity::getArmorValue()
1051{
1052 int val = 0;
1053 ItemInstanceArray items = getEquipmentSlots();
1054 for (unsigned int i = 0; i < items.length; ++i)
1055 {
1056 shared_ptr<ItemInstance> item = items[i];
1057 if (item != NULL && dynamic_cast<ArmorItem *>(item->getItem()) != NULL)
1058 {
1059 int baseProtection = ((ArmorItem *) item->getItem())->defense;
1060 val += baseProtection;
1061 }
1062 }
1063 return val;
1064}
1065
1066void LivingEntity::hurtArmor(float damage)
1067{
1068}
1069
1070float LivingEntity::getDamageAfterArmorAbsorb(DamageSource *damageSource, float damage)
1071{
1072 if (!damageSource->isBypassArmor())
1073 {
1074 int absorb = 25 - getArmorValue();
1075 float v = (damage) * absorb;
1076 hurtArmor(damage);
1077 damage = v / 25;
1078 }
1079 return damage;
1080}
1081
1082float LivingEntity::getDamageAfterMagicAbsorb(DamageSource *damageSource, float damage)
1083{
1084 // [EB]: Stupid hack :(
1085 if ( this->instanceof(eTYPE_ZOMBIE) )
1086 {
1087 damage = damage;
1088 }
1089 if (hasEffect(MobEffect::damageResistance) && damageSource != DamageSource::outOfWorld)
1090 {
1091 int absorbValue = (getEffect(MobEffect::damageResistance)->getAmplifier() + 1) * 5;
1092 int absorb = 25 - absorbValue;
1093 float v = (damage) * absorb;
1094 damage = v / 25;
1095 }
1096
1097 if (damage <= 0) return 0;
1098
1099 int enchantmentArmor = EnchantmentHelper::getDamageProtection(getEquipmentSlots(), damageSource);
1100 if (enchantmentArmor > 20)
1101 {
1102 enchantmentArmor = 20;
1103 }
1104 if (enchantmentArmor > 0 && enchantmentArmor <= 20)
1105 {
1106 int absorb = 25 - enchantmentArmor;
1107 float v = damage * absorb;
1108 damage = v / 25;
1109 }
1110
1111 return damage;
1112}
1113
1114void LivingEntity::actuallyHurt(DamageSource *source, float dmg)
1115{
1116 if (isInvulnerable()) return;
1117 dmg = getDamageAfterArmorAbsorb(source, dmg);
1118 dmg = getDamageAfterMagicAbsorb(source, dmg);
1119
1120 float originalDamage = dmg;
1121 dmg = max(dmg - getAbsorptionAmount(), 0.0f);
1122 setAbsorptionAmount(getAbsorptionAmount() - (originalDamage - dmg));
1123 if (dmg == 0) return;
1124
1125 float oldHealth = getHealth();
1126 setHealth(oldHealth - dmg);
1127 getCombatTracker()->recordDamage(source, oldHealth, dmg);
1128 setAbsorptionAmount(getAbsorptionAmount() - dmg);
1129}
1130
1131CombatTracker *LivingEntity::getCombatTracker()
1132{
1133 return combatTracker;
1134}
1135
1136shared_ptr<LivingEntity> LivingEntity::getKillCredit()
1137{
1138 if (combatTracker->getKiller() != NULL) return combatTracker->getKiller();
1139 if (lastHurtByPlayer != NULL) return lastHurtByPlayer;
1140 if (lastHurtByMob != NULL) return lastHurtByMob;
1141 return nullptr;
1142}
1143
1144float LivingEntity::getMaxHealth()
1145{
1146 return (float) getAttribute(SharedMonsterAttributes::MAX_HEALTH)->getValue();
1147}
1148
1149int LivingEntity::getArrowCount()
1150{
1151 return entityData->getByte(DATA_ARROW_COUNT_ID);
1152}
1153
1154void LivingEntity::setArrowCount(int count)
1155{
1156 entityData->set(DATA_ARROW_COUNT_ID, (byte) count);
1157}
1158
1159int LivingEntity::getCurrentSwingDuration()
1160{
1161 if (hasEffect(MobEffect::digSpeed))
1162 {
1163 return SWING_DURATION - (1 + getEffect(MobEffect::digSpeed)->getAmplifier()) * 1;
1164 }
1165 if (hasEffect(MobEffect::digSlowdown))
1166 {
1167 return SWING_DURATION + (1 + getEffect(MobEffect::digSlowdown)->getAmplifier()) * 2;
1168 }
1169 return SWING_DURATION;
1170}
1171
1172void LivingEntity::swing()
1173{
1174 if (!swinging || swingTime >= getCurrentSwingDuration() / 2 || swingTime < 0)
1175 {
1176 swingTime = -1;
1177 swinging = true;
1178
1179 if (dynamic_cast<ServerLevel *>(level) != NULL)
1180 {
1181 ((ServerLevel *) level)->getTracker()->broadcast(shared_from_this(), shared_ptr<AnimatePacket>( new AnimatePacket(shared_from_this(), AnimatePacket::SWING)));
1182 }
1183 }
1184}
1185
1186void LivingEntity::handleEntityEvent(byte id)
1187{
1188 if (id == EntityEvent::HURT)
1189 {
1190 walkAnimSpeed = 1.5f;
1191
1192 invulnerableTime = invulnerableDuration;
1193 hurtTime = hurtDuration = 10;
1194 hurtDir = 0;
1195
1196 MemSect(31);
1197 // 4J-PB -added because villagers have no sounds
1198 int iHurtSound=getHurtSound();
1199 if(iHurtSound!=-1)
1200 {
1201 playSound(iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
1202 }
1203 MemSect(0);
1204 hurt(DamageSource::genericSource, 0);
1205 }
1206 else if (id == EntityEvent::DEATH)
1207 {
1208 MemSect(31);
1209 // 4J-PB -added because villagers have no sounds
1210 int iDeathSound=getDeathSound();
1211 if(iDeathSound!=-1)
1212 {
1213 playSound(iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
1214 }
1215 MemSect(0);
1216 setHealth(0);
1217 die(DamageSource::genericSource);
1218 }
1219 else
1220 {
1221 Entity::handleEntityEvent(id);
1222 }
1223}
1224
1225void LivingEntity::outOfWorld()
1226{
1227 hurt(DamageSource::outOfWorld, 4);
1228}
1229
1230void LivingEntity::updateSwingTime()
1231{
1232 int currentSwingDuration = getCurrentSwingDuration();
1233 if (swinging)
1234 {
1235 swingTime++;
1236 if (swingTime >= currentSwingDuration)
1237 {
1238 swingTime = 0;
1239 swinging = false;
1240 }
1241 }
1242 else
1243 {
1244 swingTime = 0;
1245 }
1246
1247 attackAnim = swingTime / (float) currentSwingDuration;
1248}
1249
1250AttributeInstance *LivingEntity::getAttribute(Attribute *attribute)
1251{
1252 return getAttributes()->getInstance(attribute);
1253}
1254
1255BaseAttributeMap *LivingEntity::getAttributes()
1256{
1257 if (attributes == NULL)
1258 {
1259 attributes = new ServersideAttributeMap();
1260 }
1261
1262 return attributes;
1263}
1264
1265MobType LivingEntity::getMobType()
1266{
1267 return UNDEFINED;
1268}
1269
1270void LivingEntity::setSprinting(bool value)
1271{
1272 Entity::setSprinting(value);
1273
1274 AttributeInstance *speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED);
1275 if (speed->getModifier(eModifierId_MOB_SPRINTING) != NULL)
1276 {
1277 speed->removeModifier(eModifierId_MOB_SPRINTING);
1278 }
1279 if (value)
1280 {
1281 speed->addModifier(new AttributeModifier(*SPEED_MODIFIER_SPRINTING));
1282 }
1283}
1284
1285float LivingEntity::getSoundVolume()
1286{
1287 return 1;
1288}
1289
1290float LivingEntity::getVoicePitch()
1291{
1292 if (isBaby())
1293 {
1294 return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.5f;
1295
1296 }
1297 return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f;
1298}
1299
1300bool LivingEntity::isImmobile()
1301{
1302 return getHealth() <= 0;
1303}
1304
1305void LivingEntity::teleportTo(double x, double y, double z)
1306{
1307 moveTo(x, y, z, yRot, xRot);
1308}
1309
1310void LivingEntity::findStandUpPosition(shared_ptr<Entity> vehicle)
1311{
1312 AABB *boundingBox;
1313 double fallbackX = vehicle->x;
1314 double fallbackY = vehicle->bb->y0 + vehicle->bbHeight;
1315 double fallbackZ = vehicle->z;
1316
1317 for (double xDiff = -1.5; xDiff < 2; xDiff += 1.5)
1318 {
1319 for (double zDiff = -1.5; zDiff < 2; zDiff += 1.5)
1320 {
1321 if (xDiff == 0 && zDiff == 0)
1322 {
1323 continue;
1324 }
1325
1326 int xToInt = (int) (x + xDiff);
1327 int zToInt = (int) (z + zDiff);
1328 boundingBox = bb->cloneMove(xDiff, 1, zDiff);
1329
1330 if (level->getTileCubes(boundingBox, true)->empty())
1331 {
1332 if (level->isTopSolidBlocking(xToInt, (int) y, zToInt))
1333 {
1334 teleportTo(x + xDiff, y + 1, z + zDiff);
1335 return;
1336 }
1337 else if (level->isTopSolidBlocking(xToInt, (int) y - 1, zToInt) || level->getMaterial(xToInt, (int) y - 1, zToInt) == Material::water)
1338 {
1339 fallbackX = x + xDiff;
1340 fallbackY = y + 1;
1341 fallbackZ = z + zDiff;
1342 }
1343 }
1344 }
1345 }
1346
1347 teleportTo(fallbackX, fallbackY, fallbackZ);
1348}
1349
1350bool LivingEntity::shouldShowName()
1351{
1352 return false;
1353}
1354
1355Icon *LivingEntity::getItemInHandIcon(shared_ptr<ItemInstance> item, int layer)
1356{
1357 return item->getIcon();
1358}
1359
1360void LivingEntity::jumpFromGround()
1361{
1362 yd = 0.42f;
1363 if (hasEffect(MobEffect::jump))
1364 {
1365 yd += (getEffect(MobEffect::jump)->getAmplifier() + 1) * .1f;
1366 }
1367 if (isSprinting())
1368 {
1369 float rr = yRot * Mth::RAD_TO_GRAD;
1370
1371 xd -= Mth::sin(rr) * 0.2f;
1372 zd += Mth::cos(rr) * 0.2f;
1373 }
1374 this->hasImpulse = true;
1375}
1376
1377void LivingEntity::travel(float xa, float ya)
1378{
1379#ifdef __PSVITA__
1380 // AP - dynamic_pointer_cast is a non-trivial call
1381 Player *thisPlayer = NULL;
1382 if( this->instanceof(eTYPE_PLAYER) )
1383 {
1384 thisPlayer = (Player*) this;
1385 }
1386#else
1387 shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
1388#endif
1389 if (isInWater() && !(thisPlayer && thisPlayer->abilities.flying) )
1390 {
1391 double yo = y;
1392 moveRelative(xa, ya, useNewAi() ? 0.04f : 0.02f);
1393 move(xd, yd, zd);
1394
1395 xd *= 0.80f;
1396 yd *= 0.80f;
1397 zd *= 0.80f;
1398 yd -= 0.02;
1399
1400 if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd))
1401 {
1402 yd = 0.3f;
1403 }
1404 }
1405 else if (isInLava() && !(thisPlayer && thisPlayer->abilities.flying) )
1406 {
1407 double yo = y;
1408 moveRelative(xa, ya, 0.02f);
1409 move(xd, yd, zd);
1410 xd *= 0.50f;
1411 yd *= 0.50f;
1412 zd *= 0.50f;
1413 yd -= 0.02;
1414
1415 if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd))
1416 {
1417 yd = 0.3f;
1418 }
1419 }
1420 else
1421 {
1422 float friction = 0.91f;
1423 if (onGround)
1424 {
1425 friction = 0.6f * 0.91f;
1426 int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z));
1427 if (t > 0)
1428 {
1429 friction = Tile::tiles[t]->friction * 0.91f;
1430 }
1431 }
1432
1433 float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction);
1434
1435 float speed;
1436 if (onGround)
1437 {
1438 speed = getSpeed() * friction2;
1439 }
1440 else
1441 {
1442 speed = flyingSpeed;
1443 }
1444
1445 moveRelative(xa, ya, speed);
1446
1447 friction = 0.91f;
1448 if (onGround)
1449 {
1450 friction = 0.6f * 0.91f;
1451 int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z));
1452 if (t > 0)
1453 {
1454 friction = Tile::tiles[t]->friction * 0.91f;
1455 }
1456 }
1457 if (onLadder())
1458 {
1459 float max = 0.15f;
1460 if (xd < -max) xd = -max;
1461 if (xd > max) xd = max;
1462 if (zd < -max) zd = -max;
1463 if (zd > max) zd = max;
1464 fallDistance = 0;
1465 if (yd < -0.15) yd = -0.15;
1466 bool playerSneaking = isSneaking() && this->instanceof(eTYPE_PLAYER);
1467 if (playerSneaking && yd < 0) yd = 0;
1468 }
1469
1470 move(xd, yd, zd);
1471
1472 if (horizontalCollision && onLadder())
1473 {
1474 yd = 0.2;
1475 }
1476
1477 if (!level->isClientSide || (level->hasChunkAt((int) x, 0, (int) z) && level->getChunkAt((int) x, (int) z)->loaded))
1478 {
1479 yd -= 0.08;
1480 }
1481 else if (y > 0)
1482 {
1483 yd = -0.1;
1484 }
1485 else
1486 {
1487 yd = 0;
1488 }
1489
1490 yd *= 0.98f;
1491 xd *= friction;
1492 zd *= friction;
1493 }
1494
1495 walkAnimSpeedO = walkAnimSpeed;
1496 double xxd = x - xo;
1497 double zzd = z - zo;
1498 float wst = Mth::sqrt(xxd * xxd + zzd * zzd) * 4;
1499 if (wst > 1) wst = 1;
1500 walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f;
1501 walkAnimPos += walkAnimSpeed;
1502}
1503
1504// 4J - added for more accurate lighting of mobs. Takes a weighted average of all tiles touched by the bounding volume of the entity - the method in the Entity class (which used to be used for
1505// mobs too) simply gets a single tile's lighting value causing sudden changes of lighting values when entities go in and out of lit areas, for example when bobbing in the water.
1506int LivingEntity::getLightColor(float a)
1507{
1508 float accum[2] = {0,0};
1509 float totVol = ( bb->x1 - bb->x0 ) * ( bb->y1 - bb->y0 ) * ( bb->z1 - bb->z0 );
1510 int xmin = Mth::floor(bb->x0);
1511 int xmax = Mth::floor(bb->x1);
1512 int ymin = Mth::floor(bb->y0);
1513 int ymax = Mth::floor(bb->y1);
1514 int zmin = Mth::floor(bb->z0);
1515 int zmax = Mth::floor(bb->z1);
1516 for( int xt = xmin; xt <= xmax; xt++ )
1517 for( int yt = ymin; yt <= ymax; yt++ )
1518 for( int zt = zmin; zt <= zmax; zt++ )
1519 {
1520 float tilexmin = (float)xt;
1521 float tilexmax = (float)(xt+1);
1522 float tileymin = (float)yt;
1523 float tileymax = (float)(yt+1);
1524 float tilezmin = (float)zt;
1525 float tilezmax = (float)(zt+1);
1526 if( tilexmin < bb->x0 ) tilexmin = bb->x0;
1527 if( tilexmax > bb->x1 ) tilexmax = bb->x1;
1528 if( tileymin < bb->y0 ) tileymin = bb->y0;
1529 if( tileymax > bb->y1 ) tileymax = bb->y1;
1530 if( tilezmin < bb->z0 ) tilezmin = bb->z0;
1531 if( tilezmax > bb->z1 ) tilezmax = bb->z1;
1532 float tileVol = ( tilexmax - tilexmin ) * ( tileymax - tileymin ) * ( tilezmax - tilezmin );
1533 float frac = tileVol / totVol;
1534 int lc = level->getLightColor(xt, yt, zt, 0);
1535 accum[0] += frac * (float)( lc & 0xffff );
1536 accum[1] += frac * (float)( lc >> 16 );
1537 }
1538
1539 if( accum[0] > 240.0f ) accum[0] = 240.0f;
1540 if( accum[1] > 240.0f ) accum[1] = 240.0f;
1541
1542 return ( ( (int)accum[1])<<16) | ((int)accum[0]);
1543}
1544
1545bool LivingEntity::useNewAi()
1546{
1547 return false;
1548}
1549
1550float LivingEntity::getSpeed()
1551{
1552 if (useNewAi())
1553 {
1554 return speed;
1555 }
1556 else
1557 {
1558 return 0.1f;
1559 }
1560}
1561
1562void LivingEntity::setSpeed(float speed)
1563{
1564 this->speed = speed;
1565}
1566
1567bool LivingEntity::doHurtTarget(shared_ptr<Entity> target)
1568{
1569 setLastHurtMob(target);
1570 return false;
1571}
1572
1573bool LivingEntity::isSleeping()
1574{
1575 return false;
1576}
1577
1578void LivingEntity::tick()
1579{
1580 Entity::tick();
1581
1582 if (!level->isClientSide)
1583 {
1584 int arrowCount = getArrowCount();
1585 if (arrowCount > 0)
1586 {
1587 if (removeArrowTime <= 0)
1588 {
1589 removeArrowTime = SharedConstants::TICKS_PER_SECOND * (30 - arrowCount);
1590 }
1591 removeArrowTime--;
1592 if (removeArrowTime <= 0)
1593 {
1594 setArrowCount(arrowCount - 1);
1595 }
1596 }
1597
1598 for (int i = 0; i < 5; i++)
1599 {
1600 shared_ptr<ItemInstance> previous = lastEquipment[i];
1601 shared_ptr<ItemInstance> current = getCarried(i);
1602
1603 if (!ItemInstance::matches(current, previous))
1604 {
1605 ((ServerLevel *) level)->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEquippedItemPacket>( new SetEquippedItemPacket(entityId, i, current)));
1606 if (previous != NULL) attributes->removeItemModifiers(previous);
1607 if (current != NULL) attributes->addItemModifiers(current);
1608 lastEquipment[i] = current == NULL ? nullptr : current->copy();
1609 }
1610 }
1611 }
1612
1613 aiStep();
1614
1615 double xd = x - xo;
1616 double zd = z - zo;
1617
1618 float sideDist = xd * xd + zd * zd;
1619
1620 float yBodyRotT = yBodyRot;
1621
1622 float walkSpeed = 0;
1623 oRun = run;
1624 float tRun = 0;
1625 if (sideDist > 0.05f * 0.05f)
1626 {
1627 tRun = 1;
1628 walkSpeed = sqrt(sideDist) * 3;
1629 yBodyRotT = ((float) atan2(zd, xd) * 180 / (float) PI - 90);
1630 }
1631 if (attackAnim > 0)
1632 {
1633 yBodyRotT = yRot;
1634 }
1635 if (!onGround)
1636 {
1637 tRun = 0;
1638 }
1639 run = run + (tRun - run) * 0.3f;
1640
1641 walkSpeed = tickHeadTurn(yBodyRotT, walkSpeed);
1642
1643 while (yRot - yRotO < -180)
1644 yRotO -= 360;
1645 while (yRot - yRotO >= 180)
1646 yRotO += 360;
1647
1648 while (yBodyRot - yBodyRotO < -180)
1649 yBodyRotO -= 360;
1650 while (yBodyRot - yBodyRotO >= 180)
1651 yBodyRotO += 360;
1652
1653 while (xRot - xRotO < -180)
1654 xRotO -= 360;
1655 while (xRot - xRotO >= 180)
1656 xRotO += 360;
1657
1658 while (yHeadRot - yHeadRotO < -180)
1659 yHeadRotO -= 360;
1660 while (yHeadRot - yHeadRotO >= 180)
1661 yHeadRotO += 360;
1662
1663 animStep += walkSpeed;
1664}
1665
1666float LivingEntity::tickHeadTurn(float yBodyRotT, float walkSpeed)
1667{
1668 float yBodyRotD = Mth::wrapDegrees(yBodyRotT - yBodyRot);
1669 yBodyRot += yBodyRotD * 0.3f;
1670
1671 float headDiff = Mth::wrapDegrees(yRot - yBodyRot);
1672 bool behind = headDiff < -90 || headDiff >= 90;
1673 if (headDiff < -75) headDiff = -75;
1674 if (headDiff >= 75) headDiff = +75;
1675 yBodyRot = yRot - headDiff;
1676 if (headDiff * headDiff > 50 * 50)
1677 {
1678 yBodyRot += headDiff * 0.2f;
1679 }
1680
1681 if (behind)
1682 {
1683 walkSpeed *= -1;
1684 }
1685
1686 return walkSpeed;
1687}
1688
1689void LivingEntity::aiStep()
1690{
1691 if (noJumpDelay > 0) noJumpDelay--;
1692 if (lSteps > 0)
1693 {
1694 double xt = x + (lx - x) / lSteps;
1695 double yt = y + (ly - y) / lSteps;
1696 double zt = z + (lz - z) / lSteps;
1697
1698 double yrd = Mth::wrapDegrees(lyr - yRot);
1699 double xrd = Mth::wrapDegrees(lxr - xRot);
1700
1701 yRot += (float) ( (yrd) / lSteps );
1702 xRot += (float) ( (xrd) / lSteps );
1703
1704 lSteps--;
1705 setPos(xt, yt, zt);
1706 setRot(yRot, xRot);
1707
1708 // 4J - this collision is carried out to try and stop the lerping push the mob through the floor,
1709 // in which case gravity can then carry on moving the mob because the collision just won't work anymore.
1710 // BB for collision used to be calculated as: bb->shrink(1 / 32.0, 0, 1 / 32.0)
1711 // now using a reduced BB to try and get rid of some issues where mobs pop up the sides of walls, undersides of
1712 // trees etc.
1713 AABB *shrinkbb = bb->shrink(0.1, 0, 0.1);
1714 shrinkbb->y1 = shrinkbb->y0 + 0.1;
1715 AABBList *collisions = level->getCubes(shared_from_this(), shrinkbb);
1716 if (collisions->size() > 0)
1717 {
1718 double yTop = 0;
1719 AUTO_VAR(itEnd, collisions->end());
1720 for (AUTO_VAR(it, collisions->begin()); it != itEnd; it++)
1721 {
1722 AABB *ab = *it; //collisions->at(i);
1723 if (ab->y1 > yTop) yTop = ab->y1;
1724 }
1725
1726 yt += yTop - bb->y0;
1727 setPos(xt, yt, zt);
1728 }
1729 }
1730 else if (!isEffectiveAi())
1731 {
1732 // slow down predicted speed, to prevent mobs from sliding through
1733 // walls etc
1734 xd *= .98;
1735 yd *= .98;
1736 zd *= .98;
1737 }
1738
1739 if (abs(xd) < MIN_MOVEMENT_DISTANCE) xd = 0;
1740 if (abs(yd) < MIN_MOVEMENT_DISTANCE) yd = 0;
1741 if (abs(zd) < MIN_MOVEMENT_DISTANCE) zd = 0;
1742
1743 if (isImmobile())
1744 {
1745 jumping = false;
1746 xxa = 0;
1747 yya = 0;
1748 yRotA = 0;
1749 }
1750 else
1751 {
1752 MemSect(25);
1753 if (isEffectiveAi())
1754 {
1755 if (useNewAi())
1756 {
1757 newServerAiStep();
1758 }
1759 else
1760 {
1761 serverAiStep();
1762 yHeadRot = yRot;
1763 }
1764 }
1765 MemSect(0);
1766 }
1767
1768 if (jumping)
1769 {
1770 if (isInWater() || isInLava() )
1771 {
1772 yd += 0.04f;
1773 }
1774 else if (onGround)
1775 {
1776 if (noJumpDelay == 0)
1777 {
1778 jumpFromGround();
1779 noJumpDelay = 10;
1780 }
1781 }
1782 }
1783 else
1784 {
1785 noJumpDelay = 0;
1786 }
1787
1788
1789 xxa *= 0.98f;
1790 yya *= 0.98f;
1791 yRotA *= 0.9f;
1792
1793 travel(xxa, yya);
1794
1795 if(!level->isClientSide)
1796 {
1797 pushEntities();
1798 }
1799}
1800
1801void LivingEntity::newServerAiStep()
1802{
1803}
1804
1805void LivingEntity::pushEntities()
1806{
1807
1808 vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f));
1809 if (entities != NULL && !entities->empty())
1810 {
1811 AUTO_VAR(itEnd, entities->end());
1812 for (AUTO_VAR(it, entities->begin()); it != itEnd; it++)
1813 {
1814 shared_ptr<Entity> e = *it; //entities->at(i);
1815 if (e->isPushable()) e->push(shared_from_this());
1816 }
1817 }
1818}
1819
1820void LivingEntity::doPush(shared_ptr<Entity> e)
1821{
1822 e->push(shared_from_this());
1823}
1824
1825void LivingEntity::rideTick()
1826{
1827 Entity::rideTick();
1828 oRun = run;
1829 run = 0;
1830 fallDistance = 0;
1831}
1832
1833void LivingEntity::lerpTo(double x, double y, double z, float yRot, float xRot, int steps)
1834{
1835 heightOffset = 0;
1836 lx = x;
1837 ly = y;
1838 lz = z;
1839 lyr = yRot;
1840 lxr = xRot;
1841
1842 lSteps = steps;
1843}
1844
1845void LivingEntity::serverAiMobStep()
1846{
1847}
1848
1849void LivingEntity::serverAiStep()
1850{
1851 noActionTime++;
1852}
1853
1854void LivingEntity::setJumping(bool jump)
1855{
1856 jumping = jump;
1857}
1858
1859void LivingEntity::take(shared_ptr<Entity> e, int orgCount)
1860{
1861 if (!e->removed && !level->isClientSide)
1862 {
1863 EntityTracker *entityTracker = ((ServerLevel *) level)->getTracker();
1864 if ( e->instanceof(eTYPE_ITEMENTITY) )
1865 {
1866 entityTracker->broadcast(e, shared_ptr<TakeItemEntityPacket>( new TakeItemEntityPacket(e->entityId, entityId)));
1867 }
1868 else if ( e->instanceof(eTYPE_ARROW) )
1869 {
1870 entityTracker->broadcast(e, shared_ptr<TakeItemEntityPacket>( new TakeItemEntityPacket(e->entityId, entityId)));
1871 }
1872 else if ( e->instanceof(eTYPE_EXPERIENCEORB) )
1873 {
1874 entityTracker->broadcast(e, shared_ptr<TakeItemEntityPacket>( new TakeItemEntityPacket(e->entityId, entityId)));
1875 }
1876 }
1877}
1878
1879bool LivingEntity::canSee(shared_ptr<Entity> target)
1880{
1881 HitResult *hres = level->clip(Vec3::newTemp(x, y + getHeadHeight(), z), Vec3::newTemp(target->x, target->y + target->getHeadHeight(), target->z));
1882 bool retVal = (hres == NULL);
1883 delete hres;
1884 return retVal;
1885}
1886
1887Vec3 *LivingEntity::getLookAngle()
1888{
1889 return getViewVector(1);
1890}
1891
1892Vec3 *LivingEntity::getViewVector(float a)
1893{
1894 if (a == 1)
1895 {
1896 float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI);
1897 float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI);
1898 float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD);
1899 float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD);
1900
1901 return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos);
1902 }
1903 float xRot = xRotO + (this->xRot - xRotO) * a;
1904 float yRot = yRotO + (this->yRot - yRotO) * a;
1905
1906 float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI);
1907 float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI);
1908 float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD);
1909 float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD);
1910
1911 return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos);
1912}
1913
1914float LivingEntity::getAttackAnim(float a)
1915{
1916 float diff = attackAnim - oAttackAnim;
1917 if (diff < 0) diff += 1;
1918 return oAttackAnim + diff * a;
1919}
1920
1921Vec3 *LivingEntity::getPos(float a)
1922{
1923 if (a == 1)
1924 {
1925 return Vec3::newTemp(x, y, z);
1926 }
1927 double x = xo + (this->x - xo) * a;
1928 double y = yo + (this->y - yo) * a;
1929 double z = zo + (this->z - zo) * a;
1930
1931 return Vec3::newTemp(x, y, z);
1932}
1933
1934HitResult *LivingEntity::pick(double range, float a)
1935{
1936 Vec3 *from = getPos(a);
1937 Vec3 *b = getViewVector(a);
1938 Vec3 *to = from->add(b->x * range, b->y * range, b->z * range);
1939 return level->clip(from, to);
1940}
1941
1942bool LivingEntity::isEffectiveAi()
1943{
1944 return !level->isClientSide;
1945}
1946
1947bool LivingEntity::isPickable()
1948{
1949 return !removed;
1950}
1951
1952bool LivingEntity::isPushable()
1953{
1954 return !removed;
1955}
1956
1957float LivingEntity::getHeadHeight()
1958{
1959 return bbHeight * 0.85f;
1960}
1961
1962void LivingEntity::markHurt()
1963{
1964 hurtMarked = random->nextDouble() >= getAttribute(SharedMonsterAttributes::KNOCKBACK_RESISTANCE)->getValue();
1965}
1966
1967float LivingEntity::getYHeadRot()
1968{
1969 return yHeadRot;
1970}
1971
1972void LivingEntity::setYHeadRot(float yHeadRot)
1973{
1974 this->yHeadRot = yHeadRot;
1975}
1976
1977float LivingEntity::getAbsorptionAmount()
1978{
1979 return absorptionAmount;
1980}
1981
1982void LivingEntity::setAbsorptionAmount(float absorptionAmount)
1983{
1984 if (absorptionAmount < 0) absorptionAmount = 0;
1985 this->absorptionAmount = absorptionAmount;
1986}
1987
1988Team *LivingEntity::getTeam()
1989{
1990 return NULL;
1991}
1992
1993bool LivingEntity::isAlliedTo(shared_ptr<LivingEntity> other)
1994{
1995 return isAlliedTo(other->getTeam());
1996}
1997
1998bool LivingEntity::isAlliedTo(Team *other)
1999{
2000 if (getTeam() != NULL)
2001 {
2002 return getTeam()->isAlliedTo(other);
2003 }
2004 return false;
2005}