the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 2005 lines 48 kB view raw
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}