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