the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1841 lines 40 kB view raw
1#include "stdafx.h" 2#include "net.minecraft.world.entity.h" 3#include "net.minecraft.world.entity.monster.h" 4#include "net.minecraft.world.entity.ai.attributes.h" 5#include "net.minecraft.world.entity.ai.goal.h" 6#include "net.minecraft.world.entity.ai.navigation.h" 7#include "net.minecraft.world.entity.player.h" 8#include "net.minecraft.world.entity.monster.h" 9#include "net.minecraft.world.effect.h" 10#include "net.minecraft.world.damagesource.h" 11#include "net.minecraft.world.item.h" 12#include "net.minecraft.world.level.h" 13#include "net.minecraft.world.level.tile.h" 14#include "net.minecraft.world.inventory.h" 15#include "net.minecraft.world.phys.h" 16#include "..\Minecraft.Client\Textures.h" 17#include "..\Minecraft.Client\Minecraft.h" 18#include "BasicTypeContainers.h" 19#include "EntityHorse.h" 20 21const wstring EntityHorse::TEX_FOLDER = L"mob/horse/"; 22 23const EntitySelector *EntityHorse::PARENT_HORSE_SELECTOR = new HorseEntitySelector(); 24 25Attribute *EntityHorse::JUMP_STRENGTH = (new RangedAttribute(eAttributeId_HORSE_JUMPSTRENGTH, .7, 0, 2.0))->setSyncable(true); 26 27wstring EntityHorse::ARMOR_TEXTURES[EntityHorse::ARMORS] = {L"", L"armor/horse_armor_iron.png", L"armor/horse_armor_gold.png", L"armor/horse_armor_diamond.png"}; 28int EntityHorse::ARMOR_TEXTURES_ID[EntityHorse::ARMORS] = {-1, TN_MOB_HORSE_ARMOR_IRON, TN_MOB_HORSE_ARMOR_GOLD, TN_MOB_HORSE_ARMOR_DIAMOND }; 29wstring EntityHorse::ARMOR_HASHES[EntityHorse::ARMORS] = {L"", L"meo", L"goo", L"dio"}; 30int EntityHorse::ARMOR_PROTECTION[EntityHorse::ARMORS] = {0, 5, 7, 11}; 31 32wstring EntityHorse::VARIANT_TEXTURES[EntityHorse::VARIANTS] = {L"horse_white.png", L"horse_creamy.png", L"horse_chestnut.png", L"horse_brown.png", L"horse_black.png", L"horse_gray.png", L"horse_darkbrown.png"}; 33int EntityHorse::VARIANT_TEXTURES_ID[EntityHorse::VARIANTS] = {TN_MOB_HORSE_WHITE, TN_MOB_HORSE_CREAMY, TN_MOB_HORSE_CHESTNUT, TN_MOB_HORSE_BROWN, TN_MOB_HORSE_BLACK, TN_MOB_HORSE_GRAY, TN_MOB_HORSE_DARKBROWN}; 34 35wstring EntityHorse::VARIANT_HASHES[EntityHorse::VARIANTS] = {L"hwh", L"hcr", L"hch", L"hbr", L"hbl", L"hgr", L"hdb"}; 36 37wstring EntityHorse::MARKING_TEXTURES[EntityHorse::MARKINGS] = {L"", L"horse_markings_white.png", L"horse_markings_whitefield.png", L"horse_markings_whitedots.png", L"horse_markings_blackdots.png"}; 38int EntityHorse::MARKING_TEXTURES_ID[EntityHorse::MARKINGS] = {-1, TN_MOB_HORSE_MARKINGS_WHITE, TN_MOB_HORSE_MARKINGS_WHITEFIELD, TN_MOB_HORSE_MARKINGS_WHITEDOTS, TN_MOB_HORSE_MARKINGS_BLACKDOTS}; 39wstring EntityHorse::MARKING_HASHES[EntityHorse::MARKINGS] = {L"", L"wo_", L"wmo", L"wdo", L"bdo"}; 40 41bool HorseEntitySelector::matches(shared_ptr<Entity> entity) const 42{ 43 return entity->instanceof(eTYPE_HORSE) && dynamic_pointer_cast<EntityHorse>(entity)->isBred(); 44} 45 46EntityHorse::EntityHorse(Level *level) : Animal(level) 47{ 48 // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called 49 this->defineSynchedData(); 50 registerAttributes(); 51 setHealth(getMaxHealth()); 52 53 countEating = 0; 54 mouthCounter = 0; 55 standCounter = 0; 56 tailCounter = 0; 57 sprintCounter = 0; 58 isEntityJumping = false; 59 inventory = nullptr; 60 hasReproduced = false; 61 temper = 0; 62 playerJumpPendingScale = 0.0f; 63 allowStandSliding = false; 64 eatAnim = eatAnimO = 0.0f; 65 standAnim = standAnimO = 0.0f; 66 mouthAnim = mouthAnimO = 0.0f; 67 gallopSoundCounter = 0; 68 69 layerTextureHashName = L""; 70 71 layerTextureLayers = intArray(3); 72 for(unsigned int i = 0; i < 3; ++i) 73 { 74 layerTextureLayers[i] = -1; 75 } 76 77 setSize(1.4f, 1.6f); 78 fireImmune = false; 79 setChestedHorse(false); 80 81 getNavigation()->setAvoidWater(true); 82 goalSelector.addGoal(0, new FloatGoal(this)); 83 goalSelector.addGoal(1, new PanicGoal(this, 1.2)); 84 goalSelector.addGoal(1, new RunAroundLikeCrazyGoal(this, 1.2)); 85 goalSelector.addGoal(2, new BreedGoal(this, 1.0)); 86 goalSelector.addGoal(4, new FollowParentGoal(this, 1.0)); 87 goalSelector.addGoal(6, new RandomStrollGoal(this, .7)); 88 goalSelector.addGoal(7, new LookAtPlayerGoal(this, typeid(Player), 6)); 89 goalSelector.addGoal(8, new RandomLookAroundGoal(this)); 90 91 createInventory(); 92} 93 94EntityHorse::~EntityHorse() 95{ 96 delete [] layerTextureLayers.data; 97} 98 99void EntityHorse::defineSynchedData() 100{ 101 Animal::defineSynchedData(); 102 entityData->define(DATA_ID_HORSE_FLAGS, 0); 103 entityData->define(DATA_ID_TYPE, (byte) 0); 104 entityData->define(DATA_ID_TYPE_VARIANT, 0); 105 entityData->define(DATA_ID_OWNER_NAME, L""); 106 entityData->define(DATA_ID_ARMOR, 0); 107} 108 109void EntityHorse::setType(int i) 110{ 111 entityData->set(DATA_ID_TYPE, (byte) i); 112 clearLayeredTextureInfo(); 113} 114 115int EntityHorse::getType() 116{ 117 return entityData->getByte(DATA_ID_TYPE); 118} 119 120void EntityHorse::setVariant(int i) 121{ 122 entityData->set(DATA_ID_TYPE_VARIANT, i); 123 clearLayeredTextureInfo(); 124} 125 126int EntityHorse::getVariant() 127{ 128 return entityData->getInteger(DATA_ID_TYPE_VARIANT); 129} 130 131wstring EntityHorse::getAName() 132{ 133 if (hasCustomName()) return getCustomName(); 134#ifdef _DEBUG 135 int type = getType(); 136 switch (type) 137 { 138 default: 139 case TYPE_HORSE: 140 return L"entity.horse.name"; 141 case TYPE_DONKEY: 142 return L"entity.donkey.name"; 143 case TYPE_MULE: 144 return L"entity.mule.name"; 145 case TYPE_SKELETON: 146 return L"entity.skeletonhorse.name"; 147 case TYPE_UNDEAD: 148 return L"entity.zombiehorse.name"; 149 } 150#else 151 return L""; 152#endif 153} 154 155bool EntityHorse::getHorseFlag(int flag) 156{ 157 return (entityData->getInteger(DATA_ID_HORSE_FLAGS) & flag) != 0; 158} 159 160void EntityHorse::setHorseFlag(int flag, bool value) 161{ 162 int current = entityData->getInteger(DATA_ID_HORSE_FLAGS); 163 if (value) 164 { 165 entityData->set(DATA_ID_HORSE_FLAGS, current | flag); 166 } 167 else 168 { 169 entityData->set(DATA_ID_HORSE_FLAGS, current & ~flag); 170 } 171} 172 173bool EntityHorse::isAdult() 174{ 175 return !isBaby(); 176} 177 178bool EntityHorse::isTamed() 179{ 180 return getHorseFlag(FLAG_TAME); 181} 182 183bool EntityHorse::isRidable() 184{ 185 return isAdult(); 186} 187 188wstring EntityHorse::getOwnerName() 189{ 190 return entityData->getString(DATA_ID_OWNER_NAME); 191} 192 193void EntityHorse::setOwner(const wstring &par1Str) 194{ 195 entityData->set(DATA_ID_OWNER_NAME, par1Str); 196} 197 198float EntityHorse::getFoalScale() 199{ 200 int age = getAge(); 201 if (age >= 0) 202 { 203 return 1.0f; 204 } 205 return .5f + (float) (BABY_START_AGE - age) / (float) BABY_START_AGE * .5f; 206} 207 208 209void EntityHorse::updateSize(bool isBaby) 210{ 211 if (isBaby) 212 { 213 internalSetSize(getFoalScale()); 214 } 215 else 216 { 217 internalSetSize(1.0f); 218 } 219} 220 221bool EntityHorse::getIsJumping() 222{ 223 return isEntityJumping; 224} 225 226void EntityHorse::setTamed(bool flag) 227{ 228 setHorseFlag(FLAG_TAME, flag); 229} 230 231void EntityHorse::setIsJumping(bool flag) 232{ 233 isEntityJumping = flag; 234} 235 236 237bool EntityHorse::canBeLeashed() 238{ 239 return !isUndead() && Animal::canBeLeashed(); 240} 241 242void EntityHorse::onLeashDistance(float distanceToLeashHolder) 243{ 244 if (distanceToLeashHolder > 6 && isEating()) 245 { 246 setEating(false); 247 } 248} 249 250bool EntityHorse::isChestedHorse() 251{ 252 return getHorseFlag(FLAG_CHESTED); 253} 254 255int EntityHorse::getArmorType() 256{ 257 return entityData->getInteger(DATA_ID_ARMOR); 258} 259 260int EntityHorse::getArmorTypeForItem(shared_ptr<ItemInstance> armorItem) 261{ 262 if (armorItem == NULL) 263 { 264 return ARMOR_NONE; 265 } 266 if (armorItem->id == Item::horseArmorMetal_Id) 267 { 268 return ARMOR_IRON; 269 } 270 else if (armorItem->id == Item::horseArmorGold_Id) 271 { 272 return ARMOR_GOLD; 273 } 274 else if (armorItem->id == Item::horseArmorDiamond_Id) 275 { 276 return ARMOR_DIAMOND; 277 } 278 return ARMOR_NONE; 279} 280 281bool EntityHorse::isEating() 282{ 283 return getHorseFlag(FLAG_EATING); 284} 285 286bool EntityHorse::isStanding() 287{ 288 return getHorseFlag(FLAG_STANDING); 289} 290 291bool EntityHorse::isBred() 292{ 293 return getHorseFlag(FLAG_BRED); 294} 295 296bool EntityHorse::getHasReproduced() 297{ 298 return hasReproduced; 299} 300 301void EntityHorse::setArmorType(int i) 302{ 303 entityData->set(DATA_ID_ARMOR, i); 304 clearLayeredTextureInfo(); 305} 306 307void EntityHorse::setBred(bool flag) 308{ 309 setHorseFlag(FLAG_BRED, flag); 310 311} 312 313void EntityHorse::setChestedHorse(bool flag) 314{ 315 setHorseFlag(FLAG_CHESTED, flag); 316} 317 318void EntityHorse::setReproduced(bool flag) 319{ 320 hasReproduced = flag; 321} 322 323void EntityHorse::setSaddled(bool flag) 324{ 325 setHorseFlag(FLAG_SADDLE, flag); 326} 327 328int EntityHorse::getTemper() 329{ 330 return temper; 331} 332 333void EntityHorse::setTemper(int temper) 334{ 335 this->temper = temper; 336} 337 338int EntityHorse::modifyTemper(int amount) 339{ 340 int temper = Mth::clamp(getTemper() + amount, 0, getMaxTemper()); 341 342 setTemper(temper); 343 return temper; 344} 345 346 347bool EntityHorse::hurt(DamageSource *damagesource, float dmg) 348{ 349 // 4J: Protect owned horses from untrusted players 350 if (isTamed()) 351 { 352 shared_ptr<Entity> entity = damagesource->getDirectEntity(); 353 if (entity != NULL && entity->instanceof(eTYPE_PLAYER)) 354 { 355 shared_ptr<Player> attacker = dynamic_pointer_cast<Player>(entity); 356 attacker->canHarmPlayer(getOwnerName()); 357 } 358 } 359 360 shared_ptr<Entity> attacker = damagesource->getEntity(); 361 if (rider.lock() != NULL && (rider.lock() == (attacker) )) 362 { 363 return false; 364 } 365 366 return Animal::hurt(damagesource, dmg); 367} 368 369 370int EntityHorse::getArmorValue() 371{ 372 return ARMOR_PROTECTION[getArmorType()]; 373} 374 375 376bool EntityHorse::isPushable() 377{ 378 return rider.lock() == NULL; 379} 380 381// TODO: [EB]: Explain why this is being done - what side effect does getBiome have? 382bool EntityHorse::checkSpawningBiome() 383{ 384 int x = Mth::floor(this->x); 385 int z = Mth::floor(this->z); 386 387 level->getBiome(x, z); 388 return true; 389} 390 391/** 392* Drops a chest block if the horse is bagged 393*/ 394void EntityHorse::dropBags() 395{ 396 if (level->isClientSide || !isChestedHorse()) 397 { 398 return; 399 } 400 401 spawnAtLocation(Tile::chest_Id, 1); 402 setChestedHorse(false); 403} 404 405void EntityHorse::eatingHorse() 406{ 407 openMouth(); 408 level->playEntitySound(shared_from_this(), eSoundType_EATING, 1.0f, 1.0f + (random->nextFloat() - random->nextFloat()) * 0.2f); 409} 410 411/** 412* Changed to adjust fall damage for riders 413*/ 414void EntityHorse::causeFallDamage(float fallDistance) 415{ 416 417 if (fallDistance > 1) 418 { 419 playSound(eSoundType_MOB_HORSE_LAND, .4f, 1); 420 } 421 422 int dmg = Mth::ceil(fallDistance * .5f - 3.0f); 423 if (dmg <= 0) return; 424 425 hurt(DamageSource::fall, dmg); 426 427 if (rider.lock() != NULL) 428 { 429 rider.lock()->hurt(DamageSource::fall, dmg); 430 } 431 432 int id = level->getTile(Mth::floor(x), Mth::floor(y - 0.2 - yRotO), Mth::floor(z)); 433 if (id > 0) 434 { 435 const Tile::SoundType *stepsound = Tile::tiles[id]->soundType; 436 level->playEntitySound(shared_from_this(), stepsound->getStepSound(), stepsound->getVolume() * 0.5f, stepsound->getPitch() * 0.75f); 437 } 438} 439 440 441/** 442* Different inventory sizes depending on the kind of horse 443* 444* @return 445*/ 446int EntityHorse::getInventorySize() 447{ 448 int type = getType(); 449 if (isChestedHorse() && (type == TYPE_DONKEY || type == TYPE_MULE)) 450 { 451 return INV_BASE_COUNT + INV_DONKEY_CHEST_COUNT; 452 } 453 return INV_BASE_COUNT; 454} 455 456void EntityHorse::createInventory() 457{ 458 shared_ptr<AnimalChest> old = inventory; 459 inventory = shared_ptr<AnimalChest>( new AnimalChest(L"HorseChest", getInventorySize()) ); 460 inventory->setCustomName(getAName()); 461 if (old != NULL) 462 { 463 old->removeListener(this); 464 465 int max = min(old->getContainerSize(), inventory->getContainerSize()); 466 for (int slot = 0; slot < max; slot++) 467 { 468 shared_ptr<ItemInstance> item = old->getItem(slot); 469 if (item != NULL) 470 { 471 inventory->setItem(slot, item->copy()); 472 } 473 } 474 old = nullptr; 475 } 476 inventory->addListener(this); 477 updateEquipment(); 478} 479 480void EntityHorse::updateEquipment() 481{ 482 if (!level->isClientSide) 483 { 484 setSaddled(inventory->getItem(INV_SLOT_SADDLE) != NULL); 485 if (canWearArmor()) 486 { 487 setArmorType(getArmorTypeForItem(inventory->getItem(INV_SLOT_ARMOR))); 488 } 489 } 490} 491 492void EntityHorse::containerChanged() 493{ 494 int armorType = getArmorType(); 495 bool saddled = isSaddled(); 496 updateEquipment(); 497 if (tickCount > 20) 498 { 499 if (armorType == ARMOR_NONE && armorType != getArmorType()) 500 { 501 playSound(eSoundType_MOB_HORSE_ARMOR, .5f, 1); 502 } 503 if (!saddled && isSaddled()) 504 { 505 playSound(eSoundType_MOB_HORSE_LEATHER, .5f, 1); 506 } 507 } 508 509} 510 511 512bool EntityHorse::canSpawn() 513{ 514 checkSpawningBiome(); 515 return Animal::canSpawn(); 516} 517 518 519shared_ptr<EntityHorse> EntityHorse::getClosestMommy(shared_ptr<Entity> baby, double searchRadius) 520{ 521 double closestDistance = Double::MAX_VALUE; 522 523 shared_ptr<Entity> mommy = nullptr; 524 vector<shared_ptr<Entity> > *list = level->getEntities(baby, baby->bb->expand(searchRadius, searchRadius, searchRadius), PARENT_HORSE_SELECTOR); 525 526 for(AUTO_VAR(it,list->begin()); it != list->end(); ++it) 527 { 528 shared_ptr<Entity> horse = *it; 529 double distanceSquared = horse->distanceToSqr(baby->x, baby->y, baby->z); 530 531 if (distanceSquared < closestDistance) 532 { 533 mommy = horse; 534 closestDistance = distanceSquared; 535 } 536 } 537 delete list; 538 539 return dynamic_pointer_cast<EntityHorse>(mommy); 540} 541 542double EntityHorse::getCustomJump() 543{ 544 return getAttribute(JUMP_STRENGTH)->getValue(); 545} 546 547int EntityHorse::getDeathSound() 548{ 549 openMouth(); 550 int type = getType(); 551 if (type == TYPE_UNDEAD) 552 { 553 return eSoundType_MOB_HORSE_ZOMBIE_DEATH; //"mob.horse.zombie.death"; 554 } 555 if (type == TYPE_SKELETON) 556 { 557 return eSoundType_MOB_HORSE_SKELETON_DEATH; //"mob.horse.skeleton.death"; 558 } 559 if (type == TYPE_DONKEY || type == TYPE_MULE) 560 { 561 return eSoundType_MOB_HORSE_DONKEY_DEATH; //"mob.horse.donkey.death"; 562 } 563 return eSoundType_MOB_HORSE_DEATH; //"mob.horse.death"; 564} 565 566int EntityHorse::getDeathLoot() 567{ 568 bool flag = random->nextInt(4) == 0; 569 570 int type = getType(); 571 if (type == TYPE_SKELETON) 572 { 573 return Item::bone_Id; 574 } 575 if (type == TYPE_UNDEAD) 576 { 577 if (flag) 578 { 579 return 0; 580 } 581 return Item::rotten_flesh_Id; 582 } 583 584 return Item::leather_Id; 585} 586 587int EntityHorse::getHurtSound() 588{ 589 openMouth(); 590 { 591 if (random->nextInt(3) == 0) 592 { 593 stand(); 594 } 595 } 596 int type = getType(); 597 if (type == TYPE_UNDEAD) 598 { 599 return eSoundType_MOB_HORSE_ZOMBIE_HIT; //"mob.horse.zombie.hit"; 600 } 601 if (type == TYPE_SKELETON) 602 { 603 return eSoundType_MOB_HORSE_SKELETON_HIT; //"mob.horse.skeleton.hit"; 604 } 605 if (type == TYPE_DONKEY || type == TYPE_MULE) 606 { 607 return eSoundType_MOB_HORSE_DONKEY_HIT; //"mob.horse.donkey.hit"; 608 } 609 return eSoundType_MOB_HORSE_HIT; //"mob.horse.hit"; 610} 611 612bool EntityHorse::isSaddled() 613{ 614 return getHorseFlag(FLAG_SADDLE); 615} 616 617 618int EntityHorse::getAmbientSound() 619{ 620 openMouth(); 621 if (random->nextInt(10) == 0 && !isImmobile()) 622 { 623 stand(); 624 } 625 int type = getType(); 626 if (type == TYPE_UNDEAD) 627 { 628 return eSoundType_MOB_HORSE_ZOMBIE_IDLE; //"mob.horse.zombie.idle"; 629 } 630 if (type == TYPE_SKELETON) 631 { 632 return eSoundType_MOB_HORSE_SKELETON_IDLE; //"mob.horse.skeleton.idle"; 633 } 634 if (type == TYPE_DONKEY || type == TYPE_MULE) 635 { 636 return eSoundType_MOB_HORSE_DONKEY_IDLE; //"mob.horse.donkey.idle"; 637 } 638 return eSoundType_MOB_HORSE_IDLE; //"mob.horse.idle"; 639} 640 641/** 642* sound played when an untamed mount buckles rider 643*/ 644int EntityHorse::getMadSound() 645{ 646 openMouth(); 647 stand(); 648 int type = getType(); 649 if (type == TYPE_UNDEAD || type == TYPE_SKELETON) 650 { 651 return -1; 652 } 653 if (type == TYPE_DONKEY || type == TYPE_MULE) 654 { 655 return eSoundType_MOB_HORSE_DONKEY_ANGRY; //"mob.horse.donkey.angry"; 656 } 657 return eSoundType_MOB_HORSE_ANGRY; //"mob.horse.angry"; 658} 659 660void EntityHorse::playStepSound(int xt, int yt, int zt, int t) 661{ 662 const Tile::SoundType *soundType = Tile::tiles[t]->soundType; 663 if (level->getTile(xt, yt + 1, zt) == Tile::topSnow_Id) 664 { 665 soundType = Tile::topSnow->soundType; 666 } 667 if (!Tile::tiles[t]->material->isLiquid()) 668 { 669 int type = getType(); 670 if (rider.lock() != NULL && type != TYPE_DONKEY && type != TYPE_MULE) 671 { 672 gallopSoundCounter++; 673 if (gallopSoundCounter > 5 && gallopSoundCounter % 3 == 0) 674 { 675 playSound(eSoundType_MOB_HORSE_GALLOP, soundType->getVolume() * 0.15f, soundType->getPitch()); 676 if (type == TYPE_HORSE && random->nextInt(10) == 0) 677 { 678 playSound(eSoundType_MOB_HORSE_BREATHE, soundType->getVolume() * 0.6f, soundType->getPitch()); 679 } 680 } 681 else if (gallopSoundCounter <= 5) 682 { 683 playSound(eSoundType_MOB_HORSE_WOOD, soundType->getVolume() * 0.15f, soundType->getPitch()); 684 } 685 } 686 else if (soundType == Tile::SOUND_WOOD) 687 { 688 playSound(eSoundType_MOB_HORSE_SOFT, soundType->getVolume() * 0.15f, soundType->getPitch()); 689 } 690 else 691 { 692 playSound(eSoundType_MOB_HORSE_WOOD, soundType->getVolume() * 0.15f, soundType->getPitch()); 693 } 694 } 695} 696 697void EntityHorse::registerAttributes() 698{ 699 Animal::registerAttributes(); 700 701 getAttributes()->registerAttribute(JUMP_STRENGTH); 702 703 getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(53); 704 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.225f); 705} 706 707int EntityHorse::getMaxSpawnClusterSize() 708{ 709 return 6; 710} 711 712/** 713* How difficult is the creature to be tamed? the Higher the number, the 714* more difficult 715*/ 716int EntityHorse::getMaxTemper() 717{ 718 return 100; 719} 720 721float EntityHorse::getSoundVolume() 722{ 723 return 0.8f; 724} 725 726 727int EntityHorse::getAmbientSoundInterval() 728{ 729 return 400; 730} 731 732bool EntityHorse::hasLayeredTextures() 733{ 734 return getType() == TYPE_HORSE || getArmorType() > 0; 735} 736 737void EntityHorse::clearLayeredTextureInfo() 738{ 739 layerTextureHashName = L""; 740} 741 742void EntityHorse::rebuildLayeredTextureInfo() 743{ 744 layerTextureHashName = L"horse/"; 745 layerTextureLayers[0] = -1; 746 layerTextureLayers[1] = -1; 747 layerTextureLayers[2] = -1; 748 749 int type = getType(); 750 int variant = getVariant(); 751 int armorIndex = 2; 752 if (type == TYPE_HORSE) 753 { 754 int skin = variant & 0xFF; 755 int markings = (variant & 0xFF00) >> 8; 756 layerTextureLayers[0] = VARIANT_TEXTURES_ID[skin]; 757 layerTextureHashName += VARIANT_HASHES[skin]; 758 759 layerTextureLayers[1] = MARKING_TEXTURES_ID[markings]; 760 layerTextureHashName += MARKING_HASHES[markings]; 761 762 if(layerTextureLayers[1] == -1) 763 { 764 armorIndex = 1; 765 } 766 } 767 else 768 { 769 layerTextureLayers[0] = -1; 770 layerTextureHashName += L"_" + _toString<int>(type) + L"_"; 771 armorIndex = 1; 772 } 773 774 int armor = getArmorType(); 775 layerTextureLayers[armorIndex] = ARMOR_TEXTURES_ID[armor]; 776 layerTextureHashName += ARMOR_HASHES[armor]; 777} 778 779wstring EntityHorse::getLayeredTextureHashName() 780{ 781 if (layerTextureHashName.empty()) 782 { 783 rebuildLayeredTextureInfo(); 784 } 785 return layerTextureHashName; 786} 787 788intArray EntityHorse::getLayeredTextureLayers() 789{ 790 if (layerTextureHashName.empty()) 791 { 792 rebuildLayeredTextureInfo(); 793 } 794 return layerTextureLayers; 795} 796 797void EntityHorse::openInventory(shared_ptr<Player> player) 798{ 799 if (!level->isClientSide && (rider.lock() == NULL || rider.lock() == player) && isTamed()) 800 { 801 inventory->setCustomName(getAName()); 802 player->openHorseInventory(dynamic_pointer_cast<EntityHorse>(shared_from_this()), inventory); 803 } 804} 805 806bool EntityHorse::mobInteract(shared_ptr<Player> player) 807{ 808 shared_ptr<ItemInstance> itemstack = player->inventory->getSelected(); 809 810 if (itemstack != NULL && itemstack->id == Item::spawnEgg_Id) 811 { 812 return Animal::mobInteract(player); 813 } 814 815 if (!isTamed()) 816 { 817 if (isUndead()) 818 { 819 return false; 820 } 821 } 822 823 if (isTamed() && isAdult() && player->isSneaking()) 824 { 825 openInventory(player); 826 return true; 827 } 828 829 if (isRidable() && rider.lock() != NULL) 830 { 831 return Animal::mobInteract(player); 832 } 833 834 // consumables 835 if (itemstack != NULL) 836 { 837 bool itemUsed = false; 838 839 if (canWearArmor()) 840 { 841 int armorType = -1; 842 843 if (itemstack->id == Item::horseArmorMetal_Id) 844 { 845 armorType = ARMOR_IRON; 846 } 847 else if (itemstack->id == Item::horseArmorGold_Id) 848 { 849 armorType = ARMOR_GOLD; 850 } 851 else if (itemstack->id == Item::horseArmorDiamond_Id) 852 { 853 armorType = ARMOR_DIAMOND; 854 } 855 856 if (armorType >= 0) 857 { 858 if (!isTamed()) 859 { 860 makeMad(); 861 return true; 862 } 863 openInventory(player); 864 return true; 865 } 866 } 867 868 if (!itemUsed && !isUndead()) 869 { 870 float _heal = 0; 871 int _ageUp = 0; 872 int temper = 0; 873 874 if (itemstack->id == Item::wheat_Id) 875 { 876 _heal = 2; 877 _ageUp = 60; 878 temper = 3; 879 } 880 else if (itemstack->id == Item::sugar_Id) 881 { 882 _heal = 1; 883 _ageUp = 30; 884 temper = 3; 885 } 886 else if (itemstack->id == Item::bread_Id) 887 { 888 _heal = 7; 889 _ageUp = 180; 890 temper = 3; 891 } 892 else if (itemstack->id == Tile::hayBlock_Id) 893 { 894 _heal = 20; 895 _ageUp = 180; 896 } 897 else if (itemstack->id == Item::apple_Id) 898 { 899 _heal = 3; 900 _ageUp = 60; 901 temper = 3; 902 } 903 else if (itemstack->id == Item::carrotGolden_Id) 904 { 905 _heal = 4; 906 _ageUp = 60; 907 temper = 5; 908 if (isTamed() && getAge() == 0) 909 { 910 itemUsed = true; 911 setInLove(); 912 } 913 } 914 else if (itemstack->id == Item::apple_gold_Id) 915 { 916 _heal = 10; 917 _ageUp = 240; 918 temper = 10; 919 if (isTamed() && getAge() == 0) 920 { 921 itemUsed = true; 922 setInLove(); 923 } 924 } 925 if (getHealth() < getMaxHealth() && _heal > 0) 926 { 927 heal(_heal); 928 itemUsed = true; 929 } 930 if (!isAdult() && _ageUp > 0) 931 { 932 ageUp(_ageUp); 933 itemUsed = true; 934 } 935 if (temper > 0 && (itemUsed || !isTamed()) && temper < getMaxTemper()) 936 { 937 itemUsed = true; 938 modifyTemper(temper); 939 } 940 if (itemUsed) 941 { 942 eatingHorse(); 943 } 944 } 945 946 if (!isTamed() && !itemUsed) 947 { 948 if (itemstack != NULL && itemstack->interactEnemy(player, dynamic_pointer_cast<LivingEntity>(shared_from_this()))) 949 { 950 return true; 951 } 952 makeMad(); 953 return true; 954 } 955 956 if (!itemUsed && canWearBags() && !isChestedHorse()) 957 { 958 if (itemstack->id == Tile::chest_Id) 959 { 960 setChestedHorse(true); 961 playSound(eSoundType_MOB_CHICKENPLOP, 1.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); 962 itemUsed = true; 963 createInventory(); 964 } 965 } 966 967 if (!itemUsed && isRidable() && !isSaddled()) 968 { 969 if (itemstack->id == Item::saddle_Id) 970 { 971 openInventory(player); 972 return true; 973 } 974 } 975 976 if (itemUsed) 977 { 978 if (!player->abilities.instabuild) 979 { 980 if (--itemstack->count == 0) 981 { 982 player->inventory->setItem(player->inventory->selected, nullptr); 983 } 984 } 985 return true; 986 } 987 } 988 989 if (isRidable() && rider.lock() == NULL) 990 { 991 // for name tag items and such, we must call the item's interaction 992 // method before riding 993 if (itemstack != NULL && itemstack->interactEnemy(player, dynamic_pointer_cast<LivingEntity>(shared_from_this()))) 994 { 995 return true; 996 } 997 doPlayerRide(player); 998 999 app.DebugPrintf("<EntityHorse::mobInteract> Horse speed: %f\n", (float) (getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->getValue())); 1000 1001 return true; 1002 } 1003 else 1004 { 1005 return Animal::mobInteract(player); 1006 } 1007} 1008 1009void EntityHorse::doPlayerRide(shared_ptr<Player> player) 1010{ 1011 player->yRot = yRot; 1012 player->xRot = xRot; 1013 setEating(false); 1014 setStanding(false); 1015 if (!level->isClientSide) 1016 { 1017 player->ride(shared_from_this()); 1018 } 1019} 1020 1021/** 1022* Can this horse be trapped in an amulet? 1023*/ 1024bool EntityHorse::isAmuletHorse() 1025{ 1026 return getType() == TYPE_SKELETON; 1027} 1028 1029/** 1030* Can wear regular armor 1031*/ 1032bool EntityHorse::canWearArmor() 1033{ 1034 return getType() == TYPE_HORSE; 1035} 1036 1037/** 1038* able to carry bags 1039* 1040* @return 1041*/ 1042bool EntityHorse::canWearBags() 1043{ 1044 int type = getType(); 1045 return type == TYPE_MULE || type == TYPE_DONKEY; 1046} 1047 1048bool EntityHorse::isImmobile() 1049{ 1050 if (rider.lock() != NULL && isSaddled()) 1051 { 1052 return true; 1053 } 1054 return isEating() || isStanding(); 1055} 1056 1057/** 1058* Rare horse that can be transformed into Nightmares or Bathorses or give 1059* ghost horses on dead 1060*/ 1061bool EntityHorse::isPureBreed() 1062{ 1063 return getType() > 10 && getType() < 21; 1064} 1065 1066/** 1067* Is this an Undead Horse? 1068* 1069* @return 1070*/ 1071bool EntityHorse::isUndead() 1072{ 1073 int type = getType(); 1074 return type == TYPE_UNDEAD || type == TYPE_SKELETON; 1075} 1076 1077bool EntityHorse::isSterile() 1078{ 1079 return isUndead() || getType() == TYPE_MULE; 1080} 1081 1082 1083bool EntityHorse::isFood(shared_ptr<ItemInstance> itemInstance) 1084{ 1085 // horses have their own food behaviors in mobInterract 1086 return false; 1087} 1088 1089void EntityHorse::moveTail() 1090{ 1091 tailCounter = 1; 1092} 1093 1094int EntityHorse::nameYOffset() 1095{ 1096 if (isAdult()) 1097 { 1098 return -80; 1099 } 1100 else 1101 { 1102 return (int) (-5 - getFoalScale() * 80.0f); 1103 } 1104} 1105 1106void EntityHorse::die(DamageSource *damagesource) 1107{ 1108 Animal::die(damagesource); 1109 if (!level->isClientSide) 1110 { 1111 dropMyStuff(); 1112 } 1113} 1114 1115void EntityHorse::aiStep() 1116{ 1117 if (random->nextInt(200) == 0) 1118 { 1119 moveTail(); 1120 } 1121 1122 Animal::aiStep(); 1123 1124 if (!level->isClientSide) 1125 { 1126 if (random->nextInt(900) == 0 && deathTime == 0) 1127 { 1128 heal(1); 1129 } 1130 1131 if (!isEating() && rider.lock() == NULL && random->nextInt(300) == 0) 1132 { 1133 if (level->getTile(Mth::floor(x), Mth::floor(y) - 1, Mth::floor(z)) == Tile::grass_Id) 1134 { 1135 setEating(true); 1136 } 1137 } 1138 1139 if (isEating() && ++countEating > 50) 1140 { 1141 countEating = 0; 1142 setEating(false); 1143 } 1144 1145 if (isBred() && !isAdult() && !isEating()) 1146 { 1147 shared_ptr<EntityHorse> mommy = getClosestMommy(shared_from_this(), 16); 1148 if (mommy != NULL && distanceToSqr(mommy) > 4.0) 1149 { 1150 Path *pathentity = level->findPath(shared_from_this(), mommy, 16.0f, true, false, false, true); 1151 setPath(pathentity); 1152 } 1153 1154 } 1155 } 1156} 1157 1158void EntityHorse::tick() 1159{ 1160 Animal::tick(); 1161 1162 // if client-side data values have changed, rebuild texture info 1163 if (level->isClientSide && entityData->isDirty()) 1164 { 1165 entityData->clearDirty(); 1166 clearLayeredTextureInfo(); 1167 } 1168 1169 if (mouthCounter > 0 && ++mouthCounter > 30) 1170 { 1171 mouthCounter = 0; 1172 setHorseFlag(FLAG_OPEN_MOUTH, false); 1173 } 1174 1175 if (!level->isClientSide) 1176 { 1177 if (standCounter > 0 && ++standCounter > 20) 1178 { 1179 standCounter = 0; 1180 setStanding(false); 1181 } 1182 } 1183 1184 if (tailCounter > 0 && ++tailCounter > 8) 1185 { 1186 tailCounter = 0; 1187 } 1188 1189 if (sprintCounter > 0) 1190 { 1191 ++sprintCounter; 1192 1193 if (sprintCounter > 300) 1194 { 1195 sprintCounter = 0; 1196 } 1197 } 1198 1199 eatAnimO = eatAnim; 1200 if (isEating()) 1201 { 1202 eatAnim += (1.0f - eatAnim) * .4f + .05f; 1203 if (eatAnim > 1) 1204 { 1205 eatAnim = 1; 1206 } 1207 } 1208 else 1209 { 1210 eatAnim += (.0f - eatAnim) * .4f - .05f; 1211 if (eatAnim < 0) 1212 { 1213 eatAnim = 0; 1214 } 1215 } 1216 standAnimO = standAnim; 1217 if (isStanding()) 1218 { 1219 // standing is incompatible with eating, so lock eat anim 1220 eatAnimO = eatAnim = 0; 1221 standAnim += (1.0f - standAnim) * .4f + .05f; 1222 if (standAnim > 1) 1223 { 1224 standAnim = 1; 1225 } 1226 } 1227 else 1228 { 1229 allowStandSliding = false; 1230 // the animation falling back to ground is slower in the beginning 1231 standAnim += (.8f * standAnim * standAnim * standAnim - standAnim) * .6f - .05f; 1232 if (standAnim < 0) 1233 { 1234 standAnim = 0; 1235 } 1236 } 1237 mouthAnimO = mouthAnim; 1238 if (getHorseFlag(FLAG_OPEN_MOUTH)) 1239 { 1240 mouthAnim += (1.0f - mouthAnim) * .7f + .05f; 1241 if (mouthAnim > 1) 1242 { 1243 mouthAnim = 1; 1244 } 1245 } 1246 else 1247 { 1248 mouthAnim += (.0f - mouthAnim) * .7f - .05f; 1249 if (mouthAnim < 0) 1250 { 1251 mouthAnim = 0; 1252 } 1253 } 1254} 1255 1256void EntityHorse::openMouth() 1257{ 1258 if (!level->isClientSide) 1259 { 1260 mouthCounter = 1; 1261 setHorseFlag(FLAG_OPEN_MOUTH, true); 1262 } 1263} 1264 1265bool EntityHorse::isReadyForParenting() 1266{ 1267 return rider.lock() == NULL && riding == NULL && isTamed() && isAdult() && !isSterile() && getHealth() >= getMaxHealth(); 1268} 1269 1270bool EntityHorse::renderName() 1271{ 1272 return hasCustomName() && rider.lock() == NULL; 1273} 1274 1275bool EntityHorse::rideableEntity() 1276{ 1277 return true; 1278} 1279 1280 1281void EntityHorse::setUsingItemFlag(bool flag) 1282{ 1283 setHorseFlag(FLAG_EATING, flag); 1284} 1285 1286void EntityHorse::setEating(bool state) 1287{ 1288 setUsingItemFlag(state); 1289} 1290 1291void EntityHorse::setStanding(bool state) 1292{ 1293 if (state) 1294 { 1295 setEating(false); 1296 } 1297 setHorseFlag(FLAG_STANDING, state); 1298} 1299 1300void EntityHorse::stand() 1301{ 1302 if (!level->isClientSide) 1303 { 1304 standCounter = 1; 1305 setStanding(true); 1306 } 1307} 1308 1309void EntityHorse::makeMad() 1310{ 1311 stand(); 1312 int ambient = getMadSound(); 1313 playSound(ambient, getSoundVolume(), getVoicePitch()); 1314} 1315 1316void EntityHorse::dropMyStuff() 1317{ 1318 dropInventory(shared_from_this(), inventory); 1319 dropBags(); 1320} 1321 1322void EntityHorse::dropInventory(shared_ptr<Entity> entity, shared_ptr<AnimalChest> animalchest) 1323{ 1324 if (animalchest == NULL || level->isClientSide) return; 1325 1326 for (int i = 0; i < animalchest->getContainerSize(); i++) 1327 { 1328 shared_ptr<ItemInstance> itemstack = animalchest->getItem(i); 1329 if (itemstack == NULL) 1330 { 1331 continue; 1332 } 1333 spawnAtLocation(itemstack, 0); 1334 } 1335 1336} 1337 1338bool EntityHorse::tameWithName(shared_ptr<Player> player) 1339{ 1340 setOwner(player->getName()); 1341 setTamed(true); 1342 return true; 1343} 1344 1345/** 1346* Overridden method to add control to mounts, should be moved to 1347* EntityLiving 1348*/ 1349void EntityHorse::travel(float xa, float ya) 1350{ 1351 // If the entity is not ridden by Player, then execute the normal 1352 // Entityliving code 1353 if (rider.lock() == NULL || !isSaddled()) 1354 { 1355 footSize = .5f; 1356 flyingSpeed = .02f; 1357 Animal::travel(xa, ya); 1358 return; 1359 } 1360 1361 yRotO = yRot = rider.lock()->yRot; 1362 xRot = rider.lock()->xRot * 0.5f; 1363 setRot(yRot, xRot); 1364 yHeadRot = yBodyRot = yRot; 1365 1366 shared_ptr<LivingEntity> livingRider = dynamic_pointer_cast<LivingEntity>(rider.lock()); 1367 xa = livingRider->xxa * .5f; 1368 ya = livingRider->yya; 1369 1370 // move much slower backwards 1371 if (ya <= 0) 1372 { 1373 ya *= .25f; 1374 gallopSoundCounter = 0; 1375 } 1376 1377 if (onGround && playerJumpPendingScale == 0 && isStanding() && !allowStandSliding) 1378 { 1379 xa = 0; 1380 ya = 0; 1381 } 1382 1383 if (playerJumpPendingScale > 0 && !getIsJumping() && onGround) 1384 { 1385 yd = getCustomJump() * playerJumpPendingScale; 1386 if (hasEffect(MobEffect::jump)) 1387 { 1388 yd += (getEffect(MobEffect::jump)->getAmplifier() + 1) * .1f; 1389 } 1390 1391 setIsJumping(true); 1392 hasImpulse = true; 1393 1394 if (ya > 0) 1395 { 1396 float sin = Mth::sin(yRot * PI / 180); 1397 float cos = Mth::cos(yRot * PI / 180); 1398 1399 xd += -0.4f * sin * playerJumpPendingScale; 1400 zd += 0.4f * cos * playerJumpPendingScale; 1401 1402 playSound(eSoundType_MOB_HORSE_JUMP, .4f, 1); 1403 } 1404 playerJumpPendingScale = 0; 1405 } 1406 1407 footSize = 1; 1408 flyingSpeed = getSpeed() * .1f; 1409 if (!level->isClientSide) 1410 { 1411 setSpeed((float) (getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->getValue())); 1412 Animal::travel(xa, ya); 1413 } 1414 1415 1416 if (onGround) 1417 { 1418 // blood - fixes jump bug 1419 playerJumpPendingScale = 0; 1420 setIsJumping(false); 1421 } 1422 walkAnimSpeedO = walkAnimSpeed; 1423 double dx = x - xo; 1424 double dz = z - zo; 1425 float wst = Mth::sqrt(dx * dx + dz * dz) * 4.0f; 1426 if (wst > 1.0f) 1427 { 1428 wst = 1.0f; 1429 } 1430 1431 walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f; 1432 walkAnimPos += walkAnimSpeed; 1433 1434} 1435 1436 1437void EntityHorse::addAdditonalSaveData(CompoundTag *tag) 1438{ 1439 Animal::addAdditonalSaveData(tag); 1440 1441 tag->putBoolean(L"EatingHaystack", isEating()); 1442 tag->putBoolean(L"ChestedHorse", isChestedHorse()); 1443 tag->putBoolean(L"HasReproduced", getHasReproduced()); 1444 tag->putBoolean(L"Bred", isBred()); 1445 tag->putInt(L"Type", getType()); 1446 tag->putInt(L"Variant", getVariant()); 1447 tag->putInt(L"Temper", getTemper()); 1448 tag->putBoolean(L"Tame", isTamed()); 1449 tag->putString(L"OwnerName", getOwnerName()); 1450 1451 if (isChestedHorse()) 1452 { 1453 ListTag<CompoundTag> *listTag = new ListTag<CompoundTag>(); 1454 1455 for (int i = INV_BASE_COUNT; i < inventory->getContainerSize(); i++) 1456 { 1457 shared_ptr<ItemInstance> stack = inventory->getItem(i); 1458 1459 if (stack != NULL) 1460 { 1461 CompoundTag *compoundTag = new CompoundTag(); 1462 1463 compoundTag->putByte(L"Slot", (byte) i); 1464 1465 stack->save(compoundTag); 1466 listTag->add(compoundTag); 1467 } 1468 } 1469 tag->put(L"Items", listTag); 1470 } 1471 1472 if (inventory->getItem(INV_SLOT_ARMOR) != NULL) 1473 { 1474 tag->put(L"ArmorItem", inventory->getItem(INV_SLOT_ARMOR)->save(new CompoundTag(L"ArmorItem"))); 1475 } 1476 if (inventory->getItem(INV_SLOT_SADDLE) != NULL) 1477 { 1478 tag->put(L"SaddleItem", inventory->getItem(INV_SLOT_SADDLE)->save(new CompoundTag(L"SaddleItem"))); 1479 } 1480} 1481 1482 1483void EntityHorse::readAdditionalSaveData(CompoundTag *tag) 1484{ 1485 Animal::readAdditionalSaveData(tag); 1486 setEating(tag->getBoolean(L"EatingHaystack")); 1487 setBred(tag->getBoolean(L"Bred")); 1488 setChestedHorse(tag->getBoolean(L"ChestedHorse")); 1489 setReproduced(tag->getBoolean(L"HasReproduced")); 1490 setType(tag->getInt(L"Type")); 1491 setVariant(tag->getInt(L"Variant")); 1492 setTemper(tag->getInt(L"Temper")); 1493 setTamed(tag->getBoolean(L"Tame")); 1494 if (tag->contains(L"OwnerName")) 1495 { 1496 setOwner(tag->getString(L"OwnerName")); 1497 } 1498 1499 // 4J: This is for handling old save data, not needed on console 1500 /*AttributeInstance *oldSpeedAttribute = getAttributes()->getInstance(SharedMonsterAttributes::MOVEMENT_SPEED); 1501 1502 if (oldSpeedAttribute != NULL) 1503 { 1504 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(oldSpeedAttribute->getBaseValue() * 0.25f); 1505 }*/ 1506 1507 if (isChestedHorse()) 1508 { 1509 ListTag<CompoundTag> *nbttaglist = (ListTag<CompoundTag> *) tag->getList(L"Items"); 1510 createInventory(); 1511 1512 for (int i = 0; i < nbttaglist->size(); i++) 1513 { 1514 CompoundTag *compoundTag = nbttaglist->get(i); 1515 int slot = compoundTag->getByte(L"Slot") & 0xFF; 1516 1517 if (slot >= INV_BASE_COUNT && slot < inventory->getContainerSize()) 1518 { 1519 inventory->setItem(slot, ItemInstance::fromTag(compoundTag)); 1520 } 1521 } 1522 } 1523 1524 if (tag->contains(L"ArmorItem")) 1525 { 1526 shared_ptr<ItemInstance> armor = ItemInstance::fromTag(tag->getCompound(L"ArmorItem")); 1527 if (armor != NULL && isHorseArmor(armor->id)) 1528 { 1529 inventory->setItem(INV_SLOT_ARMOR, armor); 1530 } 1531 } 1532 1533 if (tag->contains(L"SaddleItem")) 1534 { 1535 shared_ptr<ItemInstance> saddleItem = ItemInstance::fromTag(tag->getCompound(L"SaddleItem")); 1536 if (saddleItem != NULL && saddleItem->id == Item::saddle_Id) 1537 { 1538 inventory->setItem(INV_SLOT_SADDLE, saddleItem); 1539 } 1540 } 1541 else if (tag->getBoolean(L"Saddle")) 1542 { 1543 inventory->setItem(INV_SLOT_SADDLE, shared_ptr<ItemInstance>( new ItemInstance(Item::saddle))); 1544 } 1545 updateEquipment(); 1546} 1547 1548 1549bool EntityHorse::canMate(shared_ptr<Animal> partner) 1550{ 1551 if (partner == shared_from_this()) return false; 1552 if (partner->GetType() != GetType()) return false; 1553 1554 shared_ptr<EntityHorse> horsePartner = dynamic_pointer_cast<EntityHorse>(partner); 1555 1556 if (!isReadyForParenting() || !horsePartner->isReadyForParenting()) 1557 { 1558 return false; 1559 } 1560 int type = getType(); 1561 int pType = horsePartner->getType(); 1562 1563 return type == pType || (type == TYPE_HORSE && pType == TYPE_DONKEY) || (type == TYPE_DONKEY && pType == TYPE_HORSE); 1564} 1565 1566 1567shared_ptr<AgableMob> EntityHorse::getBreedOffspring(shared_ptr<AgableMob> partner) 1568{ 1569 shared_ptr<EntityHorse> horsePartner = dynamic_pointer_cast<EntityHorse>(partner); 1570 shared_ptr<EntityHorse> baby = shared_ptr<EntityHorse>( new EntityHorse(level) ); 1571 1572 int type = getType(); 1573 int partnerType = horsePartner->getType(); 1574 int babyType = TYPE_HORSE; 1575 1576 if (type == partnerType) 1577 { 1578 babyType = type; 1579 } 1580 else if (type == TYPE_HORSE && partnerType == TYPE_DONKEY || type == TYPE_DONKEY && partnerType == TYPE_HORSE) 1581 { 1582 babyType = TYPE_MULE; 1583 } 1584 1585 // select skin and marking colors 1586 if (babyType == TYPE_HORSE) 1587 { 1588 int skinResult; 1589 int selectSkin = random->nextInt(9); 1590 if (selectSkin < 4) 1591 { 1592 skinResult = getVariant() & 0xff; 1593 } 1594 else if (selectSkin < 8) 1595 { 1596 skinResult = horsePartner->getVariant() & 0xff; 1597 } 1598 else 1599 { 1600 skinResult = random->nextInt(VARIANTS); 1601 } 1602 1603 int selectMarking = random->nextInt(5); 1604 if (selectMarking < 4) 1605 { 1606 skinResult |= getVariant() & 0xff00; 1607 } 1608 else if (selectMarking < 8) 1609 { 1610 skinResult |= horsePartner->getVariant() & 0xff00; 1611 } 1612 else 1613 { 1614 skinResult |= (random->nextInt(MARKINGS) << 8) & 0xff00; 1615 } 1616 baby->setVariant(skinResult); 1617 } 1618 1619 baby->setType(babyType); 1620 1621 // generate stats from parents 1622 double maxHealth = getAttribute(SharedMonsterAttributes::MAX_HEALTH)->getBaseValue() + partner->getAttribute(SharedMonsterAttributes::MAX_HEALTH)->getBaseValue() + generateRandomMaxHealth(); 1623 baby->getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(maxHealth / 3.0f); 1624 1625 double jumpStrength = getAttribute(JUMP_STRENGTH)->getBaseValue() + partner->getAttribute(JUMP_STRENGTH)->getBaseValue() + generateRandomJumpStrength(); 1626 baby->getAttribute(JUMP_STRENGTH)->setBaseValue(jumpStrength / 3.0f); 1627 1628 double speed = getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->getBaseValue() + partner->getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->getBaseValue() + generateRandomSpeed(); 1629 baby->getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(speed / 3.0f); 1630 1631 return baby; 1632} 1633 1634MobGroupData *EntityHorse::finalizeMobSpawn(MobGroupData *groupData, int extraData /*= 0*/) // 4J Added extraData param 1635{ 1636 groupData = Animal::finalizeMobSpawn(groupData); 1637 1638 int type = 0; 1639 int variant = 0; 1640 1641 if ( dynamic_cast<HorseGroupData *>(groupData) != NULL ) 1642 { 1643 type = ((HorseGroupData *) groupData)->horseType; 1644 variant = ((HorseGroupData *) groupData)->horseVariant & 0xff | (random->nextInt(MARKINGS) << 8); 1645 } 1646 else 1647 { 1648 if(extraData != 0) 1649 { 1650 type = extraData - 1; 1651 } 1652 else if (random->nextInt(10) == 0) 1653 { 1654 type = TYPE_DONKEY; 1655 } 1656 else 1657 { 1658 type = TYPE_HORSE; 1659 } 1660 1661 if(type == TYPE_HORSE) 1662 { 1663 int skin = random->nextInt(VARIANTS); 1664 int mark = random->nextInt(MARKINGS); 1665 variant = skin | (mark << 8); 1666 } 1667 groupData = new HorseGroupData(type, variant); 1668 } 1669 1670 setType(type); 1671 setVariant(variant); 1672 1673 if (random->nextInt(5) == 0) 1674 { 1675 setAge(AgableMob::BABY_START_AGE); 1676 } 1677 1678 if (type == TYPE_SKELETON || type == TYPE_UNDEAD) 1679 { 1680 getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(15); 1681 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.2f); 1682 } 1683 else 1684 { 1685 getAttribute(SharedMonsterAttributes::MAX_HEALTH)->setBaseValue(generateRandomMaxHealth()); 1686 if (type == TYPE_HORSE) 1687 { 1688 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(generateRandomSpeed()); 1689 } 1690 else 1691 { 1692 getAttribute(SharedMonsterAttributes::MOVEMENT_SPEED)->setBaseValue(0.175f); 1693 } 1694 } 1695 if (type == TYPE_MULE || type == TYPE_DONKEY) 1696 { 1697 getAttribute(JUMP_STRENGTH)->setBaseValue(.5f); 1698 } 1699 else 1700 { 1701 getAttribute(JUMP_STRENGTH)->setBaseValue(generateRandomJumpStrength()); 1702 } 1703 setHealth(getMaxHealth()); 1704 1705 return groupData; 1706} 1707 1708float EntityHorse::getEatAnim(float a) 1709{ 1710 return eatAnimO + (eatAnim - eatAnimO) * a; 1711} 1712 1713float EntityHorse::getStandAnim(float a) 1714{ 1715 return standAnimO + (standAnim - standAnimO) * a; 1716} 1717 1718float EntityHorse::getMouthAnim(float a) 1719{ 1720 return mouthAnimO + (mouthAnim - mouthAnimO) * a; 1721} 1722 1723bool EntityHorse::useNewAi() 1724{ 1725 return true; 1726} 1727 1728void EntityHorse::onPlayerJump(int jumpAmount) 1729{ 1730 if (isSaddled()) 1731 { 1732 if (jumpAmount < 0) 1733 { 1734 jumpAmount = 0; 1735 } 1736 else 1737 { 1738 allowStandSliding = true; 1739 stand(); 1740 } 1741 1742 if (jumpAmount >= 90) 1743 { 1744 playerJumpPendingScale = 1.0f; 1745 } 1746 else 1747 { 1748 playerJumpPendingScale = .4f + .4f * (float) jumpAmount / 90.0f; 1749 } 1750 } 1751} 1752 1753void EntityHorse::spawnTamingParticles(bool success) 1754{ 1755 ePARTICLE_TYPE particle = success ? eParticleType_heart : eParticleType_smoke; 1756 1757 for (int i = 0; i < 7; i++) 1758 { 1759 double xa = random->nextGaussian() * 0.02; 1760 double ya = random->nextGaussian() * 0.02; 1761 double za = random->nextGaussian() * 0.02; 1762 level->addParticle(particle, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); 1763 } 1764} 1765 1766void EntityHorse::handleEntityEvent(byte id) 1767{ 1768 if (id == EntityEvent::TAMING_SUCCEEDED) 1769 { 1770 spawnTamingParticles(true); 1771 } 1772 else if (id == EntityEvent::TAMING_FAILED) 1773 { 1774 spawnTamingParticles(false); 1775 } 1776 else 1777 { 1778 Animal::handleEntityEvent(id); 1779 } 1780} 1781 1782void EntityHorse::positionRider() 1783{ 1784 Animal::positionRider(); 1785 1786 if (standAnimO > 0) 1787 { 1788 float sin = Mth::sin(yBodyRot * PI / 180); 1789 float cos = Mth::cos(yBodyRot * PI / 180); 1790 float dist = .7f * standAnimO; 1791 float height = .15f * standAnimO; 1792 1793 rider.lock()->setPos(x + dist * sin, y + getRideHeight() + rider.lock()->getRidingHeight() + height, z - dist * cos); 1794 1795 if ( rider.lock()->instanceof(eTYPE_LIVINGENTITY) ) 1796 { 1797 shared_ptr<LivingEntity> livingRider = dynamic_pointer_cast<LivingEntity>(rider.lock()); 1798 livingRider->yBodyRot = yBodyRot; 1799 } 1800 } 1801} 1802 1803// Health is between 15 and 30 1804float EntityHorse::generateRandomMaxHealth() 1805{ 1806 return 15.0f + random->nextInt(8) + random->nextInt(9); 1807} 1808 1809double EntityHorse::generateRandomJumpStrength() 1810{ 1811 return .4f + random->nextDouble() * .2 + random->nextDouble() * .2 + random->nextDouble() * .2; 1812} 1813 1814double EntityHorse::generateRandomSpeed() 1815{ 1816 double speed = (0.45f + random->nextDouble() * .3 + random->nextDouble() * .3 + random->nextDouble() * .3) * 0.25f; 1817 app.DebugPrintf("<EntityHorse::generateRandomSpeed> Speed: %f\n", speed); 1818 return speed; 1819} 1820 1821EntityHorse::HorseGroupData::HorseGroupData(int type, int variant) 1822{ 1823 horseType = type; 1824 horseVariant = variant; 1825} 1826 1827bool EntityHorse::isHorseArmor(int itemId) 1828{ 1829 return itemId == Item::horseArmorMetal_Id || itemId == Item::horseArmorGold_Id || itemId == Item::horseArmorDiamond_Id; 1830} 1831 1832bool EntityHorse::onLadder() 1833{ 1834 // prevent horses from climbing ladders 1835 return false; 1836} 1837 1838shared_ptr<Player> EntityHorse::getOwner() 1839{ 1840 return level->getPlayerByUUID(getOwnerName()); 1841}