the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "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}