the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1077 lines 25 kB view raw
1#include "stdafx.h" 2#include "JavaMath.h" 3#include "net.minecraft.network.packet.h" 4#include "net.minecraft.world.level.tile.h" 5#include "net.minecraft.world.phys.h" 6#include "net.minecraft.world.entity.h" 7#include "net.minecraft.world.entity.ai.attributes.h" 8#include "net.minecraft.world.entity.ai.control.h" 9#include "net.minecraft.world.entity.ai.navigation.h" 10#include "net.minecraft.world.entity.ai.sensing.h" 11#include "net.minecraft.world.entity.player.h" 12#include "net.minecraft.world.entity.animal.h" 13#include "net.minecraft.world.entity.monster.h" 14#include "net.minecraft.world.item.h" 15#include "net.minecraft.world.level.h" 16#include "net.minecraft.world.level.material.h" 17#include "net.minecraft.world.damagesource.h" 18#include "net.minecraft.world.effect.h" 19#include "net.minecraft.world.item.alchemy.h" 20#include "net.minecraft.world.item.enchantment.h" 21#include "net.minecraft.world.h" 22#include "..\Minecraft.Client\ServerLevel.h" 23#include "..\Minecraft.Client\EntityTracker.h" 24#include "com.mojang.nbt.h" 25#include "Mob.h" 26#include "..\Minecraft.Client\Textures.h" 27#include "SoundTypes.h" 28#include "BasicTypeContainers.h" 29#include "ParticleTypes.h" 30#include "GenericStats.h" 31#include "ItemEntity.h" 32 33const float Mob::MAX_WEARING_ARMOR_CHANCE = 0.15f; 34const float Mob::MAX_PICKUP_LOOT_CHANCE = 0.55f; 35const float Mob::MAX_ENCHANTED_ARMOR_CHANCE = 0.50f; 36const float Mob::MAX_ENCHANTED_WEAPON_CHANCE = 0.25f; 37 38void Mob::_init() 39{ 40 ambientSoundTime = 0; 41 xpReward = 0; 42 defaultLookAngle = 0.0f; 43 lookingAt = nullptr; 44 lookTime = 0; 45 target = nullptr; 46 sensing = NULL; 47 48 equipment = ItemInstanceArray(5); 49 dropChances = floatArray(5); 50 for(unsigned int i = 0; i < 5; ++i) 51 { 52 equipment[i] = nullptr; 53 dropChances[i] = 0.0f; 54 } 55 56 _canPickUpLoot = false; 57 persistenceRequired = false; 58 59 _isLeashed = false; 60 leashHolder = nullptr; 61 leashInfoTag = NULL; 62} 63 64Mob::Mob( Level* level) : LivingEntity(level) 65{ 66 MemSect(57); 67 _init(); 68 MemSect(0); 69 70 MemSect(58); 71 // 4J Stu - We call this again in the derived classes, but need to do it here for some internal members 72 registerAttributes(); 73 MemSect(0); 74 75 lookControl = new LookControl(this); 76 moveControl = new MoveControl(this); 77 jumpControl = new JumpControl(this); 78 bodyControl = new BodyControl(this); 79 navigation = new PathNavigation(this, level); 80 sensing = new Sensing(this); 81 82 for (int i = 0; i < 5; i++) 83 { 84 dropChances[i] = 0.085f; 85 } 86} 87 88Mob::~Mob() 89{ 90 if(lookControl != NULL) delete lookControl; 91 if(moveControl != NULL) delete moveControl; 92 if(jumpControl != NULL) delete jumpControl; 93 if(bodyControl != NULL) delete bodyControl; 94 if(navigation != NULL) delete navigation; 95 if(sensing != NULL) delete sensing; 96 97 if(leashInfoTag != NULL) delete leashInfoTag; 98 99 if(equipment.data != NULL) delete [] equipment.data; 100 delete [] dropChances.data; 101} 102 103void Mob::registerAttributes() 104{ 105 LivingEntity::registerAttributes(); 106 107 getAttributes()->registerAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->setBaseValue(16); 108} 109 110LookControl *Mob::getLookControl() 111{ 112 return lookControl; 113} 114 115MoveControl *Mob::getMoveControl() 116{ 117 return moveControl; 118} 119 120JumpControl *Mob::getJumpControl() 121{ 122 return jumpControl; 123} 124 125PathNavigation *Mob::getNavigation() 126{ 127 return navigation; 128} 129 130Sensing *Mob::getSensing() 131{ 132 return sensing; 133} 134 135shared_ptr<LivingEntity> Mob::getTarget() 136{ 137 return target; 138} 139 140void Mob::setTarget(shared_ptr<LivingEntity> target) 141{ 142 this->target = target; 143} 144 145bool Mob::canAttackType(eINSTANCEOF targetType) 146{ 147 return !(targetType == eTYPE_CREEPER || targetType == eTYPE_GHAST); 148} 149 150// Called by eatTileGoal 151void Mob::ate() 152{ 153} 154 155void Mob::defineSynchedData() 156{ 157 LivingEntity::defineSynchedData(); 158 entityData->define(DATA_CUSTOM_NAME_VISIBLE, (byte) 0); 159 entityData->define(DATA_CUSTOM_NAME, L""); 160} 161 162int Mob::getAmbientSoundInterval() 163{ 164 return 20 * 4; 165} 166 167void Mob::playAmbientSound() 168{ 169 MemSect(31); 170 int ambient = getAmbientSound(); 171 if (ambient != -1) 172 { 173 playSound(ambient, getSoundVolume(), getVoicePitch()); 174 } 175 MemSect(0); 176} 177 178void Mob::baseTick() 179{ 180 LivingEntity::baseTick(); 181 182 if (isAlive() && random->nextInt(1000) < ambientSoundTime++) 183 { 184 ambientSoundTime = -getAmbientSoundInterval(); 185 186 playAmbientSound(); 187 } 188} 189 190int Mob::getExperienceReward(shared_ptr<Player> killedBy) 191{ 192 if (xpReward > 0) 193 { 194 int result = xpReward; 195 196 ItemInstanceArray slots = getEquipmentSlots(); 197 for (int i = 0; i < slots.length; i++) 198 { 199 if (slots[i] != NULL && dropChances[i] <= 1) 200 { 201 result += 1 + random->nextInt(3); 202 } 203 } 204 205 return result; 206 } 207 else 208 { 209 return xpReward; 210 } 211} 212void Mob::spawnAnim() 213{ 214 for (int i = 0; i < 20; i++) 215 { 216 double xa = random->nextGaussian() * 0.02; 217 double ya = random->nextGaussian() * 0.02; 218 double za = random->nextGaussian() * 0.02; 219 double dd = 10; 220 level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth - xa * dd, y + random->nextFloat() * bbHeight - ya * dd, z + random->nextFloat() * bbWidth * 2 - bbWidth - za 221 * dd, xa, ya, za); 222 } 223} 224 225void Mob::tick() 226{ 227 LivingEntity::tick(); 228 229 if (!level->isClientSide) 230 { 231 tickLeash(); 232 } 233} 234 235float Mob::tickHeadTurn(float yBodyRotT, float walkSpeed) 236{ 237 if (useNewAi()) 238 { 239 bodyControl->clientTick(); 240 return walkSpeed; 241 } 242 else 243 { 244 return LivingEntity::tickHeadTurn(yBodyRotT, walkSpeed); 245 } 246} 247 248int Mob::getAmbientSound() 249{ 250 return -1; 251} 252 253int Mob::getDeathLoot() 254{ 255 return 0; 256} 257 258void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) 259{ 260 int loot = getDeathLoot(); 261 if (loot > 0) 262 { 263 int count = random->nextInt(3); 264 if (playerBonusLevel > 0) 265 { 266 count += random->nextInt(playerBonusLevel + 1); 267 } 268 for (int i = 0; i < count; i++) 269 spawnAtLocation(loot, 1); 270 } 271} 272 273void Mob::addAdditonalSaveData(CompoundTag *entityTag) 274{ 275 LivingEntity::addAdditonalSaveData(entityTag); 276 entityTag->putBoolean(L"CanPickUpLoot", canPickUpLoot()); 277 entityTag->putBoolean(L"PersistenceRequired", persistenceRequired); 278 279 ListTag<CompoundTag> *gear = new ListTag<CompoundTag>(); 280 for (int i = 0; i < equipment.length; i++) 281 { 282 CompoundTag *tag = new CompoundTag(); 283 if (equipment[i] != NULL) equipment[i]->save(tag); 284 gear->add(tag); 285 } 286 entityTag->put(L"Equipment", gear); 287 288 ListTag<FloatTag> *dropChanceList = new ListTag<FloatTag>(); 289 for (int i = 0; i < dropChances.length; i++) 290 { 291 dropChanceList->add(new FloatTag( _toString(i), dropChances[i])); 292 } 293 entityTag->put(L"DropChances", dropChanceList); 294 entityTag->putString(L"CustomName", getCustomName()); 295 entityTag->putBoolean(L"CustomNameVisible", isCustomNameVisible()); 296 297 // leash info 298 entityTag->putBoolean(L"Leashed", _isLeashed); 299 if (leashHolder != NULL) 300 { 301 CompoundTag *leashTag = new CompoundTag(L"Leash"); 302 if ( leashHolder->instanceof(eTYPE_LIVINGENTITY) ) 303 { 304 // a walking, talking, leash holder 305 leashTag->putString(L"UUID", leashHolder->getUUID()); 306 } 307 else if ( leashHolder->instanceof(eTYPE_HANGING_ENTITY) ) 308 { 309 // a fixed holder (that doesn't save itself) 310 shared_ptr<HangingEntity> hangInThere = dynamic_pointer_cast<HangingEntity>(leashHolder); 311 leashTag->putInt(L"X", hangInThere->xTile); 312 leashTag->putInt(L"Y", hangInThere->yTile); 313 leashTag->putInt(L"Z", hangInThere->zTile); 314 } 315 entityTag->put(L"Leash", leashTag); 316 } 317} 318 319void Mob::readAdditionalSaveData(CompoundTag *tag) 320{ 321 LivingEntity::readAdditionalSaveData(tag); 322 323 setCanPickUpLoot(tag->getBoolean(L"CanPickUpLoot")); 324 persistenceRequired = tag->getBoolean(L"PersistenceRequired"); 325 if (tag->contains(L"CustomName") && tag->getString(L"CustomName").length() > 0) setCustomName(tag->getString(L"CustomName")); 326 setCustomNameVisible(tag->getBoolean(L"CustomNameVisible")); 327 328 if (tag->contains(L"Equipment")) 329 { 330 ListTag<CompoundTag> *gear = (ListTag<CompoundTag> *) tag->getList(L"Equipment"); 331 332 for (int i = 0; i < equipment.length; i++) 333 { 334 equipment[i] = ItemInstance::fromTag(gear->get(i)); 335 } 336 } 337 338 if (tag->contains(L"DropChances")) 339 { 340 ListTag<FloatTag> *items = (ListTag<FloatTag> *) tag->getList(L"DropChances"); 341 for (int i = 0; i < items->size(); i++) 342 { 343 dropChances[i] = items->get(i)->data; 344 } 345 } 346 347 _isLeashed = tag->getBoolean(L"Leashed"); 348 if (_isLeashed && tag->contains(L"Leash")) 349 { 350 leashInfoTag = (CompoundTag *)tag->getCompound(L"Leash")->copy(); 351 } 352} 353 354void Mob::setYya(float yya) 355{ 356 this->yya = yya; 357} 358 359void Mob::setSpeed(float speed) 360{ 361 LivingEntity::setSpeed(speed); 362 setYya(speed); 363} 364 365void Mob::aiStep() 366{ 367 LivingEntity::aiStep(); 368 369 if (!level->isClientSide && canPickUpLoot() && !dead && level->getGameRules()->getBoolean(GameRules::RULE_MOBGRIEFING)) 370 { 371 vector<shared_ptr<Entity> > *entities = level->getEntitiesOfClass(typeid(ItemEntity), bb->grow(1, 0, 1)); 372 for (AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) 373 { 374 shared_ptr<ItemEntity> entity = dynamic_pointer_cast<ItemEntity>(*it); 375 if (entity->removed || entity->getItem() == NULL) continue; 376 shared_ptr<ItemInstance> item = entity->getItem(); 377 int slot = getEquipmentSlotForItem(item); 378 379 if (slot > -1) 380 { 381 bool replace = true; 382 shared_ptr<ItemInstance> current = getCarried(slot); 383 384 if (current != NULL) 385 { 386 if (slot == SLOT_WEAPON) 387 { 388 WeaponItem *newWeapon = dynamic_cast<WeaponItem *>(item->getItem()); 389 WeaponItem *oldWeapon = dynamic_cast<WeaponItem *>(current->getItem()); 390 if ( newWeapon != NULL && oldWeapon == NULL) 391 { 392 replace = true; 393 } 394 else if (newWeapon != NULL && oldWeapon != NULL) 395 { 396 if (newWeapon->getTierDamage() == oldWeapon->getTierDamage()) 397 { 398 replace = item->getAuxValue() > current->getAuxValue() || item->hasTag() && !current->hasTag(); 399 } 400 else 401 { 402 replace = newWeapon->getTierDamage() > oldWeapon->getTierDamage(); 403 } 404 } 405 else 406 { 407 replace = false; 408 } 409 } 410 else 411 { 412 ArmorItem *newArmor = dynamic_cast<ArmorItem *>(item->getItem()); 413 ArmorItem *oldArmor = dynamic_cast<ArmorItem *>(current->getItem()); 414 if (newArmor != NULL && oldArmor == NULL) 415 { 416 replace = true; 417 } 418 else if (newArmor != NULL && oldArmor != NULL) 419 { 420 if (newArmor->defense == oldArmor->defense) 421 { 422 replace = item->getAuxValue() > current->getAuxValue() || item->hasTag() && !current->hasTag(); 423 } 424 else 425 { 426 replace = newArmor->defense > oldArmor->defense; 427 } 428 } 429 else 430 { 431 replace = false; 432 } 433 } 434 } 435 436 if (replace) 437 { 438 if (current != NULL && random->nextFloat() - 0.1f < dropChances[slot]) 439 { 440 spawnAtLocation(current, 0); 441 } 442 443 setEquippedSlot(slot, item); 444 dropChances[slot] = 2; 445 persistenceRequired = true; 446 take(entity, 1); 447 entity->remove(); 448 } 449 } 450 } 451 delete entities; 452 } 453} 454 455bool Mob::useNewAi() 456{ 457 return false; 458} 459 460bool Mob::removeWhenFarAway() 461{ 462 return true; 463} 464 465void Mob::checkDespawn() 466{ 467 if (persistenceRequired) 468 { 469 noActionTime = 0; 470 return; 471 } 472 shared_ptr<Entity> player = level->getNearestPlayer(shared_from_this(), -1); 473 if (player != NULL) 474 { 475 double xd = player->x - x; 476 double yd = player->y - y; 477 double zd = player->z - z; 478 double sd = xd * xd + yd * yd + zd * zd; 479 480 if (removeWhenFarAway() && sd > 128 * 128) 481 { 482 remove(); 483 } 484 485 if (noActionTime > 20 * 30 && random->nextInt(800) == 0 && sd > 32 * 32 && removeWhenFarAway()) 486 { 487 remove(); 488 } 489 else if (sd < 32 * 32) 490 { 491 noActionTime = 0; 492 } 493 } 494} 495 496void Mob::newServerAiStep() 497{ 498 PIXBeginNamedEvent(0,"Tick target selector for %d",GetType()); 499 MemSect(51); 500 noActionTime++; 501 PIXBeginNamedEvent(0,"Check despawn"); 502 checkDespawn(); 503 PIXEndNamedEvent(); 504 PIXBeginNamedEvent(0,"Tick sensing"); 505 sensing->tick(); 506 PIXEndNamedEvent(); 507 PIXBeginNamedEvent(0,"Tick target selector"); 508 targetSelector.tick(); 509 PIXEndNamedEvent(); 510 PIXBeginNamedEvent(0,"Tick goal selectors"); 511 goalSelector.tick(); 512 PIXEndNamedEvent(); 513 PIXBeginNamedEvent(0,"Tick navigation"); 514 navigation->tick(); 515 PIXEndNamedEvent(); 516 PIXBeginNamedEvent(0,"Tick server ai mob step"); 517 serverAiMobStep(); 518 PIXEndNamedEvent(); 519 PIXBeginNamedEvent(0,"Tick move"); 520 moveControl->tick(); 521 PIXEndNamedEvent(); 522 PIXBeginNamedEvent(0,"Tick look"); 523 lookControl->tick(); 524 PIXEndNamedEvent(); 525 PIXBeginNamedEvent(0,"Tick jump"); 526 jumpControl->tick(); 527 PIXEndNamedEvent(); 528 // Consider this for extra strolling if it is protected against despawning. We aren't interested in ones that aren't protected as the whole point of this 529 // extra wandering is to potentially transition from protected to not protected. 530 PIXBeginNamedEvent(0,"Consider extra wandering"); 531 considerForExtraWandering( isDespawnProtected() ); 532 PIXEndNamedEvent(); 533 MemSect(0); 534 PIXEndNamedEvent(); 535} 536 537void Mob::serverAiStep() 538{ 539 LivingEntity::serverAiStep(); 540 541 xxa = 0; 542 yya = 0; 543 544 checkDespawn(); 545 546 float lookDistance = 8; 547 if (random->nextFloat() < 0.02f) 548 { 549 shared_ptr<Player> player = level->getNearestPlayer(shared_from_this(), lookDistance); 550 if (player != NULL) 551 { 552 lookingAt = player; 553 lookTime = 10 + random->nextInt(20); 554 } 555 else 556 { 557 yRotA = (random->nextFloat() - 0.5f) * 20; 558 } 559 } 560 561 if (lookingAt != NULL) 562 { 563 lookAt(lookingAt, 10.0f, (float) getMaxHeadXRot()); 564 if (lookTime-- <= 0 || lookingAt->removed || lookingAt->distanceToSqr(shared_from_this()) > lookDistance * lookDistance) 565 { 566 lookingAt = nullptr; 567 } 568 } 569 else 570 { 571 if (random->nextFloat() < 0.05f) 572 { 573 yRotA = (random->nextFloat() - 0.5f) * 20; 574 } 575 yRot += yRotA; 576 xRot = defaultLookAngle; 577 } 578 579 bool inWater = isInWater(); 580 bool inLava = isInLava(); 581 if (inWater || inLava) jumping = random->nextFloat() < 0.8f; 582} 583 584int Mob::getMaxHeadXRot() 585{ 586 return 40; 587} 588 589void Mob::lookAt(shared_ptr<Entity> e, float yMax, float xMax) 590{ 591 double xd = e->x - x; 592 double yd; 593 double zd = e->z - z; 594 595 596 if ( e->instanceof(eTYPE_LIVINGENTITY) ) 597 { 598 shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(e); 599 yd = (mob->y + mob->getHeadHeight()) - (y + getHeadHeight()); 600 } 601 else 602 { 603 yd = (e->bb->y0 + e->bb->y1) / 2 - (y + getHeadHeight()); 604 } 605 606 double sd = Mth::sqrt(xd * xd + zd * zd); 607 608 float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90; 609 float xRotD = (float) -(atan2(yd, sd) * 180 / PI); 610 xRot = rotlerp(xRot, xRotD, xMax); 611 yRot = rotlerp(yRot, yRotD, yMax); 612} 613 614bool Mob::isLookingAtAnEntity() 615{ 616 return lookingAt != NULL; 617} 618 619shared_ptr<Entity> Mob::getLookingAt() 620{ 621 return lookingAt; 622} 623 624float Mob::rotlerp(float a, float b, float max) 625{ 626 float diff = Mth::wrapDegrees(b - a); 627 if (diff > max) 628 { 629 diff = max; 630 } 631 if (diff < -max) 632 { 633 diff = -max; 634 } 635 return a + diff; 636} 637 638bool Mob::canSpawn() 639{ 640 // 4J - altered to use special containsAnyLiquid variant 641 return level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid_NoLoad(bb); 642} 643 644float Mob::getSizeScale() 645{ 646 return 1.0f; 647} 648 649float Mob::getHeadSizeScale() 650{ 651 return 1.0f; 652} 653 654int Mob::getMaxSpawnClusterSize() 655{ 656 return 4; 657} 658 659int Mob::getMaxFallDistance() 660{ 661 if (getTarget() == NULL) return 3; 662 int sacrifice = (int) (getHealth() - (getMaxHealth() * 0.33f)); 663 sacrifice -= (3 - level->difficulty) * 4; 664 if (sacrifice < 0) sacrifice = 0; 665 return sacrifice + 3; 666} 667 668shared_ptr<ItemInstance> Mob::getCarriedItem() 669{ 670 return equipment[SLOT_WEAPON]; 671} 672 673shared_ptr<ItemInstance> Mob::getCarried(int slot) 674{ 675 return equipment[slot]; 676} 677 678shared_ptr<ItemInstance> Mob::getArmor(int pos) 679{ 680 return equipment[pos + 1]; 681} 682 683void Mob::setEquippedSlot(int slot, shared_ptr<ItemInstance> item) 684{ 685 equipment[slot] = item; 686} 687 688ItemInstanceArray Mob::getEquipmentSlots() 689{ 690 return equipment; 691} 692 693void Mob::dropEquipment(bool byPlayer, int playerBonusLevel) 694{ 695 for (int slot = 0; slot < getEquipmentSlots().length; slot++) 696 { 697 shared_ptr<ItemInstance> item = getCarried(slot); 698 bool preserve = dropChances[slot] > 1; 699 700 if (item != NULL && (byPlayer || preserve) && random->nextFloat() - playerBonusLevel * 0.01f < dropChances[slot]) 701 { 702 if (!preserve && item->isDamageableItem()) 703 { 704 int _max = max(item->getMaxDamage() - 25, 1); 705 int damage = item->getMaxDamage() - random->nextInt(random->nextInt(_max) + 1); 706 if (damage > _max) damage = _max; 707 if (damage < 1) damage = 1; 708 item->setAuxValue(damage); 709 } 710 spawnAtLocation(item, 0); 711 } 712 } 713} 714 715void Mob::populateDefaultEquipmentSlots() 716{ 717 if (random->nextFloat() < MAX_WEARING_ARMOR_CHANCE * level->getDifficulty(x, y, z)) 718 { 719 int armorType = random->nextInt(2); 720 float partialChance = level->difficulty == Difficulty::HARD ? 0.1f : 0.25f; 721 if (random->nextFloat() < 0.095f) armorType++; 722 if (random->nextFloat() < 0.095f) armorType++; 723 if (random->nextFloat() < 0.095f) armorType++; 724 725 for (int i = 3; i >= 0; i--) 726 { 727 shared_ptr<ItemInstance> item = getArmor(i); 728 if (i < 3 && random->nextFloat() < partialChance) break; 729 if (item == NULL) 730 { 731 Item *equip = getEquipmentForSlot(i + 1, armorType); 732 if (equip != NULL) setEquippedSlot(i + 1, shared_ptr<ItemInstance>(new ItemInstance(equip))); 733 } 734 } 735 } 736} 737 738int Mob::getEquipmentSlotForItem(shared_ptr<ItemInstance> item) 739{ 740 if (item->id == Tile::pumpkin_Id || item->id == Item::skull_Id) 741 { 742 return SLOT_HELM; 743 } 744 745 ArmorItem *armorItem = dynamic_cast<ArmorItem *>(item->getItem()); 746 if (armorItem != NULL) 747 { 748 switch (armorItem->slot) 749 { 750 case ArmorItem::SLOT_FEET: 751 return SLOT_BOOTS; 752 case ArmorItem::SLOT_LEGS: 753 return SLOT_LEGGINGS; 754 case ArmorItem::SLOT_TORSO: 755 return SLOT_CHEST; 756 case ArmorItem::SLOT_HEAD: 757 return SLOT_HELM; 758 } 759 } 760 761 return SLOT_WEAPON; 762} 763 764Item *Mob::getEquipmentForSlot(int slot, int type) 765{ 766 switch (slot) 767 { 768 case SLOT_HELM: 769 if (type == 0) return Item::helmet_leather; 770 if (type == 1) return Item::helmet_gold; 771 if (type == 2) return Item::helmet_chain; 772 if (type == 3) return Item::helmet_iron; 773 if (type == 4) return Item::helmet_diamond; 774 case SLOT_CHEST: 775 if (type == 0) return Item::chestplate_leather; 776 if (type == 1) return Item::chestplate_gold; 777 if (type == 2) return Item::chestplate_chain; 778 if (type == 3) return Item::chestplate_iron; 779 if (type == 4) return Item::chestplate_diamond; 780 case SLOT_LEGGINGS: 781 if (type == 0) return Item::leggings_leather; 782 if (type == 1) return Item::leggings_gold; 783 if (type == 2) return Item::leggings_chain; 784 if (type == 3) return Item::leggings_iron; 785 if (type == 4) return Item::leggings_diamond; 786 case SLOT_BOOTS: 787 if (type == 0) return Item::boots_leather; 788 if (type == 1) return Item::boots_gold; 789 if (type == 2) return Item::boots_chain; 790 if (type == 3) return Item::boots_iron; 791 if (type == 4) return Item::boots_diamond; 792 } 793 794 return NULL; 795} 796 797void Mob::populateDefaultEquipmentEnchantments() 798{ 799 float difficulty = level->getDifficulty(x, y, z); 800 801 if (getCarriedItem() != NULL && random->nextFloat() < MAX_ENCHANTED_WEAPON_CHANCE * difficulty) { 802 EnchantmentHelper::enchantItem(random, getCarriedItem(), (int) (5 + difficulty * random->nextInt(18))); 803 } 804 805 for (int i = 0; i < 4; i++) 806 { 807 shared_ptr<ItemInstance> item = getArmor(i); 808 if (item != NULL && random->nextFloat() < MAX_ENCHANTED_ARMOR_CHANCE * difficulty) 809 { 810 EnchantmentHelper::enchantItem(random, item, (int) (5 + difficulty * random->nextInt(18))); 811 } 812 } 813} 814 815/** 816* Added this method so mobs can handle their own spawn settings instead of 817* hacking MobSpawner.java 818* 819* @param groupData 820* TODO 821* @return TODO 822*/ 823MobGroupData *Mob::finalizeMobSpawn(MobGroupData *groupData, int extraData /*= 0*/) // 4J Added extraData param 824{ 825 // 4J Stu - Take this out, it's not great and nobody will notice. Also not great for performance. 826 //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->addModifier(new AttributeModifier(random->nextGaussian() * 0.05, AttributeModifier::OPERATION_MULTIPLY_BASE)); 827 828 return groupData; 829} 830 831void Mob::finalizeSpawnEggSpawn(int extraData) 832{ 833} 834 835bool Mob::canBeControlledByRider() 836{ 837 return false; 838} 839 840wstring Mob::getAName() 841{ 842 if (hasCustomName()) return getCustomName(); 843 return LivingEntity::getAName(); 844} 845 846void Mob::setPersistenceRequired() 847{ 848 persistenceRequired = true; 849} 850 851void Mob::setCustomName(const wstring &name) 852{ 853 entityData->set(DATA_CUSTOM_NAME, name); 854} 855 856wstring Mob::getCustomName() 857{ 858 return entityData->getString(DATA_CUSTOM_NAME); 859} 860 861bool Mob::hasCustomName() 862{ 863 return entityData->getString(DATA_CUSTOM_NAME).length() > 0; 864} 865 866void Mob::setCustomNameVisible(bool visible) 867{ 868 entityData->set(DATA_CUSTOM_NAME_VISIBLE, visible ? (byte) 1 : (byte) 0); 869} 870 871bool Mob::isCustomNameVisible() 872{ 873 return entityData->getByte(DATA_CUSTOM_NAME_VISIBLE) == 1; 874} 875 876bool Mob::shouldShowName() 877{ 878 return isCustomNameVisible(); 879} 880 881void Mob::setDropChance(int slot, float pct) 882{ 883 dropChances[slot] = pct; 884} 885 886bool Mob::canPickUpLoot() 887{ 888 return _canPickUpLoot; 889} 890 891void Mob::setCanPickUpLoot(bool canPickUpLoot) 892{ 893 _canPickUpLoot = canPickUpLoot; 894} 895 896bool Mob::isPersistenceRequired() 897{ 898 return persistenceRequired; 899} 900 901bool Mob::interact(shared_ptr<Player> player) 902{ 903 904 if (isLeashed() && getLeashHolder() == player) 905 { 906 dropLeash(true, !player->abilities.instabuild); 907 return true; 908 } 909 910 shared_ptr<ItemInstance> itemstack = player->inventory->getSelected(); 911 if (itemstack != NULL) 912 { 913 // it's inconvenient to have the leash code here, but it's because 914 // the mob.interact(player) method has priority over 915 // item.interact(mob) 916 if (itemstack->id == Item::lead_Id) 917 { 918 if (canBeLeashed()) 919 { 920 shared_ptr<TamableAnimal> tamableAnimal = nullptr; 921 if ( shared_from_this()->instanceof(eTYPE_TAMABLE_ANIMAL) 922 && (tamableAnimal = dynamic_pointer_cast<TamableAnimal>(shared_from_this()))->isTame() ) // 4J-JEV: excuse the assignment operator in here, don't want to dyn-cast if it's avoidable. 923 { 924 if (player->getUUID().compare(tamableAnimal->getOwnerUUID()) == 0) 925 { 926 setLeashedTo(player, true); 927 itemstack->count--; 928 return true; 929 } 930 } 931 else 932 { 933 setLeashedTo(player, true); 934 itemstack->count--; 935 return true; 936 } 937 } 938 } 939 } 940 941 if (mobInteract(player)) 942 { 943 return true; 944 } 945 946 return LivingEntity::interact(player); 947} 948 949bool Mob::mobInteract(shared_ptr<Player> player) 950{ 951 return false; 952} 953 954void Mob::tickLeash() 955{ 956 if (leashInfoTag != NULL) 957 { 958 restoreLeashFromSave(); 959 } 960 if (!_isLeashed) 961 { 962 return; 963 } 964 965 if (leashHolder == NULL || leashHolder->removed) 966 { 967 dropLeash(true, true); 968 return; 969 } 970} 971 972void Mob::dropLeash(bool synch, bool createItemDrop) 973{ 974 if (_isLeashed) 975 { 976 _isLeashed = false; 977 leashHolder = nullptr; 978 if (!level->isClientSide && createItemDrop) 979 { 980 spawnAtLocation(Item::lead_Id, 1); 981 } 982 983 ServerLevel *serverLevel = dynamic_cast<ServerLevel *>(level); 984 if (!level->isClientSide && synch && serverLevel != NULL) 985 { 986 serverLevel->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEntityLinkPacket>(new SetEntityLinkPacket(SetEntityLinkPacket::LEASH, shared_from_this(), nullptr))); 987 } 988 } 989} 990 991bool Mob::canBeLeashed() 992{ 993 return !isLeashed() && !shared_from_this()->instanceof(eTYPE_ENEMY); 994} 995 996bool Mob::isLeashed() 997{ 998 return _isLeashed; 999} 1000 1001shared_ptr<Entity> Mob::getLeashHolder() 1002{ 1003 return leashHolder; 1004} 1005 1006void Mob::setLeashedTo(shared_ptr<Entity> holder, bool synch) 1007{ 1008 _isLeashed = true; 1009 leashHolder = holder; 1010 1011 ServerLevel *serverLevel = dynamic_cast<ServerLevel *>(level); 1012 if (!level->isClientSide && synch && serverLevel) 1013 { 1014 serverLevel->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEntityLinkPacket>( new SetEntityLinkPacket(SetEntityLinkPacket::LEASH, shared_from_this(), leashHolder))); 1015 } 1016} 1017 1018void Mob::restoreLeashFromSave() 1019{ 1020 // after being added to the world, attempt to recreate leash bond 1021 if (_isLeashed && leashInfoTag != NULL) 1022 { 1023 if (leashInfoTag->contains(L"UUID")) 1024 { 1025 wstring leashUuid = leashInfoTag->getString(L"UUID"); 1026 vector<shared_ptr<Entity> > *livingEnts = level->getEntitiesOfClass(typeid(LivingEntity), bb->grow(10, 10, 10)); 1027 for(AUTO_VAR(it, livingEnts->begin()); it != livingEnts->end(); ++it) 1028 { 1029 shared_ptr<LivingEntity> le = dynamic_pointer_cast<LivingEntity>(*it); 1030 if (le->getUUID().compare(leashUuid) == 0) 1031 { 1032 leashHolder = le; 1033 setLeashedTo(leashHolder, true); 1034 break; 1035 } 1036 } 1037 delete livingEnts; 1038 } 1039 else if (leashInfoTag->contains(L"X") && leashInfoTag->contains(L"Y") && leashInfoTag->contains(L"Z")) 1040 { 1041 int x = leashInfoTag->getInt(L"X"); 1042 int y = leashInfoTag->getInt(L"Y"); 1043 int z = leashInfoTag->getInt(L"Z"); 1044 1045 shared_ptr<LeashFenceKnotEntity> activeKnot = LeashFenceKnotEntity::findKnotAt(level, x, y, z); 1046 if (activeKnot == NULL) 1047 { 1048 activeKnot = LeashFenceKnotEntity::createAndAddKnot(level, x, y, z); 1049 } 1050 leashHolder = activeKnot; 1051 setLeashedTo(leashHolder, true); 1052 } 1053 else 1054 { 1055 dropLeash(false, true); 1056 } 1057 } 1058 leashInfoTag = NULL; 1059} 1060 1061// 4J added so we can not render mobs before their chunks are loaded - to resolve bug 10327 :Gameplay: NPCs can spawn over chunks that have not yet been streamed and display jitter. 1062bool Mob::shouldRender(Vec3 *c) 1063{ 1064 if( !level->reallyHasChunksAt( Mth::floor(bb->x0), Mth::floor(bb->y0), Mth::floor(bb->z0), Mth::floor(bb->x1), Mth::floor(bb->y1), Mth::floor(bb->z1))) 1065 { 1066 return false; 1067 } 1068 return Entity::shouldRender(c); 1069} 1070 1071void Mob::setLevel(Level *level) 1072{ 1073 Entity::setLevel(level); 1074 navigation->setLevel(level); 1075 goalSelector.setLevel(level); 1076 targetSelector.setLevel(level); 1077}