aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.World/Mob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Minecraft.World/Mob.cpp')
-rw-r--r--Minecraft.World/Mob.cpp1936
1 files changed, 535 insertions, 1401 deletions
diff --git a/Minecraft.World/Mob.cpp b/Minecraft.World/Mob.cpp
index d2dcddfb..cac25ddb 100644
--- a/Minecraft.World/Mob.cpp
+++ b/Minecraft.World/Mob.cpp
@@ -1,8 +1,10 @@
#include "stdafx.h"
#include "JavaMath.h"
+#include "net.minecraft.network.packet.h"
#include "net.minecraft.world.level.tile.h"
#include "net.minecraft.world.phys.h"
#include "net.minecraft.world.entity.h"
+#include "net.minecraft.world.entity.ai.attributes.h"
#include "net.minecraft.world.entity.ai.control.h"
#include "net.minecraft.world.entity.ai.navigation.h"
#include "net.minecraft.world.entity.ai.sensing.h"
@@ -16,6 +18,9 @@
#include "net.minecraft.world.effect.h"
#include "net.minecraft.world.item.alchemy.h"
#include "net.minecraft.world.item.enchantment.h"
+#include "net.minecraft.world.h"
+#include "..\Minecraft.Client\ServerLevel.h"
+#include "..\Minecraft.Client\EntityTracker.h"
#include "com.mojang.nbt.h"
#include "Mob.h"
#include "..\Minecraft.Client\Textures.h"
@@ -25,134 +30,63 @@
#include "GenericStats.h"
#include "ItemEntity.h"
-const double Mob::MIN_MOVEMENT_DISTANCE = 0.005;
+const float Mob::MAX_WEARING_ARMOR_CHANCE = 0.15f;
+const float Mob::MAX_PICKUP_LOOT_CHANCE = 0.55f;
+const float Mob::MAX_ENCHANTED_ARMOR_CHANCE = 0.50f;
+const float Mob::MAX_ENCHANTED_WEAPON_CHANCE = 0.25f;
void Mob::_init()
{
- invulnerableDuration = 20;
- timeOffs = 0.0f;
-
- yBodyRot = 0;
- yBodyRotO = 0;
- yHeadRot = 0;
- yHeadRotO = 0;
-
- oRun = 0.0f;
- run = 0.0f;
-
- animStep = 0.0f;
- animStepO = 0.0f;
-
- MemSect(31);
- hasHair = true;
- textureIdx = TN_MOB_CHAR; // 4J was L"/mob/char.png";
- allowAlpha = true;
- rotOffs = 0;
- modelName = L"";
- bobStrength = 1;
- deathScore = 0;
- renderOffset = 0;
- MemSect(0);
-
- walkingSpeed = 0.1f;
- flyingSpeed = 0.02f;
-
- oAttackAnim = 0.0f;
- attackAnim = 0.0f;
-
- lastHealth = 0;
- dmgSpill = 0;
-
ambientSoundTime = 0;
-
- hurtTime = 0;
- hurtDuration = 0;
- hurtDir = 0;
- deathTime = 0;
- attackTime = 0;
- oTilt = 0;
- tilt = 0;
-
- dead = false;
xpReward = 0;
-
- modelNum = -1;
- animSpeed = (float) (Math::random() * 0.9f + 0.1f);
-
- walkAnimSpeedO = 0.0f;
- walkAnimSpeed = 0.0f;
- walkAnimPos = 0.0f;
-
- lastHurtByPlayer = nullptr;
- lastHurtByPlayerTime = 0;
- lastHurtByMob = nullptr;
- lastHurtByMobTime = 0;
- lastHurtMob = nullptr;
-
- arrowCount = 0;
- removeArrowTime = 0;
-
- lSteps = 0;
- lx = ly = lz = lyr = lxr = 0.0;
-
- fallTime = 0.0f;
-
- lastHurt = 0;
-
- noActionTime = 0;
- xxa = yya = yRotA = 0.0f;
- jumping = false;
defaultLookAngle = 0.0f;
- runSpeed = 0.7f;
- noJumpDelay = 0;
-
lookingAt = nullptr;
lookTime = 0;
-
- effectsDirty = true;
- effectColor = 0;
-
target = nullptr;
sensing = NULL;
- speed = 0.0f;
- restrictCenter = new Pos(0, 0, 0);
- restrictRadius = -1.0f;
+ equipment = ItemInstanceArray(5);
+ dropChances = floatArray(5);
+ for(unsigned int i = 0; i < 5; ++i)
+ {
+ equipment[i] = nullptr;
+ dropChances[i] = 0.0f;
+ }
+
+ _canPickUpLoot = false;
+ persistenceRequired = false;
+
+ _isLeashed = false;
+ leashHolder = nullptr;
+ leashInfoTag = NULL;
}
-Mob::Mob( Level* level) : Entity(level)
+Mob::Mob( Level* level) : LivingEntity(level)
{
+ MemSect(57);
_init();
+ MemSect(0);
- // 4J Stu - This will not call the correct derived function, so moving to each derived class
- //health = getMaxHealth();
- health = 0;
-
- blocksBuilding = true;
+ MemSect(58);
+ // 4J Stu - We call this again in the derived classes, but need to do it here for some internal members
+ registerAttributes();
+ MemSect(0);
lookControl = new LookControl(this);
moveControl = new MoveControl(this);
jumpControl = new JumpControl(this);
bodyControl = new BodyControl(this);
- navigation = new PathNavigation(this, level, 16);
+ navigation = new PathNavigation(this, level);
sensing = new Sensing(this);
- rotA = (float) (Math::random() + 1) * 0.01f;
- setPos(x, y, z);
- timeOffs = (float) Math::random() * 12398;
- yRot = (float) (Math::random() * PI * 2);
- yHeadRot = yRot;
-
- this->footSize = 0.5f;
+ for (int i = 0; i < 5; i++)
+ {
+ dropChances[i] = 0.085f;
+ }
}
Mob::~Mob()
{
- for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it)
- {
- delete it->second;
- }
-
if(lookControl != NULL) delete lookControl;
if(moveControl != NULL) delete moveControl;
if(jumpControl != NULL) delete jumpControl;
@@ -160,7 +94,17 @@ Mob::~Mob()
if(navigation != NULL) delete navigation;
if(sensing != NULL) delete sensing;
- delete restrictCenter;
+ if(leashInfoTag != NULL) delete leashInfoTag;
+
+ if(equipment.data != NULL) delete [] equipment.data;
+ delete [] dropChances.data;
+}
+
+void Mob::registerAttributes()
+{
+ LivingEntity::registerAttributes();
+
+ getAttributes()->registerAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->setBaseValue(16);
}
LookControl *Mob::getLookControl()
@@ -188,65 +132,12 @@ Sensing *Mob::getSensing()
return sensing;
}
-Random *Mob::getRandom()
-{
- return random;
-}
-
-shared_ptr<Mob> Mob::getLastHurtByMob()
-{
- return lastHurtByMob;
-}
-
-shared_ptr<Mob> Mob::getLastHurtMob()
-{
- return lastHurtMob;
-}
-
-void Mob::setLastHurtMob(shared_ptr<Entity> target)
-{
- shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(target);
- if (mob != NULL) lastHurtMob = mob;
-}
-
-int Mob::getNoActionTime()
-{
- return noActionTime;
-}
-
-float Mob::getYHeadRot()
-{
- return yHeadRot;
-}
-
-void Mob::setYHeadRot(float yHeadRot)
-{
- this->yHeadRot = yHeadRot;
-}
-
-float Mob::getSpeed()
-{
- return speed;
-}
-
-void Mob::setSpeed(float speed)
-{
- this->speed = speed;
- setYya(speed);
-}
-
-bool Mob::doHurtTarget(shared_ptr<Entity> target)
-{
- setLastHurtMob(target);
- return false;
-}
-
-shared_ptr<Mob> Mob::getTarget()
+shared_ptr<LivingEntity> Mob::getTarget()
{
return target;
}
-void Mob::setTarget(shared_ptr<Mob> target)
+void Mob::setTarget(shared_ptr<LivingEntity> target)
{
this->target = target;
}
@@ -261,81 +152,11 @@ void Mob::ate()
{
}
-// might move to navigation, might make area
-bool Mob::isWithinRestriction()
-{
- return isWithinRestriction(Mth::floor(x), Mth::floor(y), Mth::floor(z));
-}
-
-bool Mob::isWithinRestriction(int x, int y, int z)
-{
- if (restrictRadius == -1) return true;
- return restrictCenter->distSqr(x, y, z) < restrictRadius * restrictRadius;
-}
-
-void Mob::restrictTo(int x, int y, int z, int radius)
-{
- restrictCenter->set(x, y, z);
- restrictRadius = radius;
-}
-
-Pos *Mob::getRestrictCenter()
-{
- return restrictCenter;
-}
-
-float Mob::getRestrictRadius()
-{
- return restrictRadius;
-}
-
-void Mob::clearRestriction()
-{
- restrictRadius = -1;
-}
-
-bool Mob::hasRestriction()
-{
- return restrictRadius != -1;
-}
-
-void Mob::setLastHurtByMob(shared_ptr<Mob> hurtBy)
-{
- lastHurtByMob = hurtBy;
- lastHurtByMobTime = lastHurtByMob != NULL ? PLAYER_HURT_EXPERIENCE_TIME : 0;
-}
-
void Mob::defineSynchedData()
{
- entityData->define(DATA_EFFECT_COLOR_ID, effectColor);
-}
-
-bool Mob::canSee(shared_ptr<Entity> target)
-{
- HitResult *hres = level->clip(Vec3::newTemp(x, y + getHeadHeight(), z), Vec3::newTemp(target->x, target->y + target->getHeadHeight(), target->z));
- bool retVal = (hres == NULL);
- delete hres;
- return retVal;
-}
-
-int Mob::getTexture()
-{
- return textureIdx;
-}
-
-bool Mob::isPickable()
-{
- return !removed;
-}
-
-bool Mob::isPushable()
-{
- return !removed;
-}
-
-float Mob::getHeadHeight()
-{
- return bbHeight * 0.85f;
+ LivingEntity::defineSynchedData();
+ entityData->define(DATA_CUSTOM_NAME_VISIBLE, (byte) 0);
+ entityData->define(DATA_CUSTOM_NAME, L"");
}
int Mob::getAmbientSoundInterval()
@@ -349,15 +170,14 @@ void Mob::playAmbientSound()
int ambient = getAmbientSound();
if (ambient != -1)
{
- level->playSound(shared_from_this(), ambient, getSoundVolume(), getVoicePitch());
+ playSound(ambient, getSoundVolume(), getVoicePitch());
}
MemSect(0);
}
void Mob::baseTick()
{
- oAttackAnim = attackAnim;
- Entity::baseTick();
+ LivingEntity::baseTick();
if (isAlive() && random->nextInt(1000) < ambientSoundTime++)
{
@@ -365,126 +185,30 @@ void Mob::baseTick()
playAmbientSound();
}
+}
- if (isAlive() && isInWall())
+int Mob::getExperienceReward(shared_ptr<Player> killedBy)
+{
+ if (xpReward > 0)
{
- hurt(DamageSource::inWall, 1);
- }
-
- if (isFireImmune() || level->isClientSide) clearFire();
+ int result = xpReward;
- if (isAlive() && isUnderLiquid(Material::water) && !isWaterMob() && activeEffects.find(MobEffect::waterBreathing->id) == activeEffects.end())
- {
- setAirSupply(decreaseAirSupply(getAirSupply()));
- if (getAirSupply() == -20)
+ ItemInstanceArray slots = getEquipmentSlots();
+ for (int i = 0; i < slots.length; i++)
{
- setAirSupply(0);
- if(canCreateParticles())
+ if (slots[i] != NULL && dropChances[i] <= 1)
{
- for (int i = 0; i < 8; i++)
- {
- float xo = random->nextFloat() - random->nextFloat();
- float yo = random->nextFloat() - random->nextFloat();
- float zo = random->nextFloat() - random->nextFloat();
- level->addParticle(eParticleType_bubble, x + xo, y + yo, z + zo, xd, yd, zd);
- }
+ result += 1 + random->nextInt(3);
}
- hurt(DamageSource::drown, 2);
}
- clearFire();
- }
- else
- {
- setAirSupply(TOTAL_AIR_SUPPLY);
+ return result;
}
-
- oTilt = tilt;
-
- if (attackTime > 0) attackTime--;
- if (hurtTime > 0) hurtTime--;
- if (invulnerableTime > 0) invulnerableTime--;
- if (health <= 0)
- {
- tickDeath();
- }
-
- if (lastHurtByPlayerTime > 0) lastHurtByPlayerTime--;
else
{
- // Note - this used to just set to nullptr, but that has to create a new shared_ptr and free an old one, when generally this won't be doing anything at all. This
- // is the lightweight but ugly alternative
- if( lastHurtByPlayer )
- {
- lastHurtByPlayer.reset();
- }
+ return xpReward;
}
- if (lastHurtMob != NULL && !lastHurtMob->isAlive()) lastHurtMob = nullptr;
-
- if (lastHurtByMob != NULL)
- {
- if (!lastHurtByMob->isAlive()) setLastHurtByMob(nullptr);
- else if (lastHurtByMobTime > 0) lastHurtByMobTime--;
- else setLastHurtByMob(nullptr);
- }
-
- // update effects
- tickEffects();
-
- animStepO = animStep;
-
- yBodyRotO = yBodyRot;
- yHeadRotO = yHeadRot;
- yRotO = yRot;
- xRotO = xRot;
}
-
-void Mob::tickDeath()
-{
- deathTime++;
- if (deathTime == 20)
- {
- // 4J Stu - Added level->isClientSide check from 1.2 to fix XP orbs being created client side
- if(!level->isClientSide && (lastHurtByPlayerTime > 0 || isAlwaysExperienceDropper()) )
- {
- if (!isBaby())
- {
- int xpCount = this->getExperienceReward(lastHurtByPlayer);
- while (xpCount > 0)
- {
- int newCount = ExperienceOrb::getExperienceValue(xpCount);
- xpCount -= newCount;
- level->addEntity(shared_ptr<ExperienceOrb>( new ExperienceOrb(level, x, y, z, newCount) ) );
- }
- }
- }
-
- remove();
- for (int i = 0; i < 20; i++)
- {
- double xa = random->nextGaussian() * 0.02;
- double ya = random->nextGaussian() * 0.02;
- double za = random->nextGaussian() * 0.02;
- level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
- }
- }
-}
-
-int Mob::decreaseAirSupply(int currentSupply)
-{
- return currentSupply - 1;
-}
-
-int Mob::getExperienceReward(shared_ptr<Player> killedBy)
-{
- return xpReward;
-}
-
-bool Mob::isAlwaysExperienceDropper()
-{
- return false;
-}
-
void Mob::spawnAnim()
{
for (int i = 0; i < 20; i++)
@@ -498,325 +222,27 @@ void Mob::spawnAnim()
}
}
-void Mob::rideTick()
-{
- Entity::rideTick();
- oRun = run;
- run = 0;
- fallDistance = 0;
-}
-
-void Mob::lerpTo(double x, double y, double z, float yRot, float xRot, int steps)
-{
- heightOffset = 0;
- lx = x;
- ly = y;
- lz = z;
- lyr = yRot;
- lxr = xRot;
-
- lSteps = steps;
-}
-
-void Mob::superTick()
-{
- Entity::tick();
-}
-
void Mob::tick()
{
- Entity::tick();
+ LivingEntity::tick();
- if (arrowCount > 0)
- {
- if (removeArrowTime <= 0)
- {
- removeArrowTime = 20 * 3;
- }
- removeArrowTime--;
- if (removeArrowTime <= 0)
- {
- arrowCount--;
- }
- }
-
- aiStep();
-
- double xd = x - xo;
- double zd = z - zo;
-
- float sideDist = xd * xd + zd * zd;
-
- float yBodyRotT = yBodyRot;
-
- float walkSpeed = 0;
- oRun = run;
- float tRun = 0;
- if (sideDist <= 0.05f * 0.05f)
- {
- // animStep = 0;
- }
- else
- {
- tRun = 1;
- walkSpeed = sqrt(sideDist) * 3;
- yBodyRotT = ((float) atan2(zd, xd) * 180 / (float) PI - 90);
- }
- if (attackAnim > 0)
- {
- yBodyRotT = yRot;
- }
- if (!onGround)
+ if (!level->isClientSide)
{
- tRun = 0;
+ tickLeash();
}
- run = run + (tRun - run) * 0.3f;
-
- /*
- * float yBodyRotD = yRot-yBodyRot; while (yBodyRotD < -180) yBodyRotD
- * += 360; while (yBodyRotD >= 180) yBodyRotD -= 360; yBodyRot +=
- * yBodyRotD * 0.1f;
- */
+}
+float Mob::tickHeadTurn(float yBodyRotT, float walkSpeed)
+{
if (useNewAi())
{
bodyControl->clientTick();
+ return walkSpeed;
}
else
{
- float yBodyRotD = Mth::wrapDegrees(yBodyRotT - yBodyRot);
- yBodyRot += yBodyRotD * 0.3f;
-
- float headDiff = Mth::wrapDegrees(yRot - yBodyRot);
- bool behind = headDiff < -90 || headDiff >= 90;
- if (headDiff < -75) headDiff = -75;
- if (headDiff >= 75) headDiff = +75;
- yBodyRot = yRot - headDiff;
- if (headDiff * headDiff > 50 * 50)
- {
- yBodyRot += headDiff * 0.2f;
- }
-
- if (behind)
- {
- walkSpeed *= -1;
- }
- }
- while (yRot - yRotO < -180)
- yRotO -= 360;
- while (yRot - yRotO >= 180)
- yRotO += 360;
-
- while (yBodyRot - yBodyRotO < -180)
- yBodyRotO -= 360;
- while (yBodyRot - yBodyRotO >= 180)
- yBodyRotO += 360;
-
- while (xRot - xRotO < -180)
- xRotO -= 360;
- while (xRot - xRotO >= 180)
- xRotO += 360;
-
- while (yHeadRot - yHeadRotO < -180)
- yHeadRotO -= 360;
- while (yHeadRot - yHeadRotO >= 180)
- yHeadRotO += 360;
-
- animStep += walkSpeed;
-}
-
-void Mob::heal(int heal)
-{
- if (health <= 0) return;
- health += heal;
- if (health > getMaxHealth()) health = getMaxHealth();
- invulnerableTime = invulnerableDuration / 2;
-}
-
-int Mob::getHealth()
-{
- return health;
-}
-
-void Mob::setHealth(int health)
-{
- this->health = health;
- if (health > getMaxHealth())
- {
- health = getMaxHealth();
- }
-}
-
-bool Mob::hurt(DamageSource *source, int dmg)
-{
- // 4J Stu - Reworked this function a bit to show hurt damage on the client before the server responds.
- // Fix for #8823 - Gameplay: Confirmation that a monster or animal has taken damage from an attack is highly delayed
- // 4J Stu - Change to the fix to only show damage when attacked, rather than collision damage
- // Fix for #10299 - When in corners, passive mobs may show that they are taking damage.
- // 4J Stu - Change to the fix for TU6, as source is never NULL due to changes in 1.8.2 to what source actually is
- if (level->isClientSide && dynamic_cast<EntityDamageSource *>(source) == NULL) return false;
- noActionTime = 0;
- if (health <= 0) return false;
-
- if ( source->isFire() && hasEffect(MobEffect::fireResistance) )
- {
- // 4J-JEV, for new achievement Stayin'Frosty, TODO merge with Java version.
- shared_ptr<Player> plr = dynamic_pointer_cast<Player>(shared_from_this());
- if ( plr != NULL && source == DamageSource::lava ) // Only award when in lava (not any fire).
- {
- plr->awardStat(GenericStats::stayinFrosty(),GenericStats::param_stayinFrosty());
- }
- return false;
- }
-
- this->walkAnimSpeed = 1.5f;
-
- bool sound = true;
- if (invulnerableTime > invulnerableDuration / 2.0f)
- {
- if (dmg <= lastHurt) return false;
- if(!level->isClientSide) actuallyHurt(source, dmg - lastHurt);
- lastHurt = dmg;
- sound = false;
- }
- else
- {
- lastHurt = dmg;
- lastHealth = health;
- invulnerableTime = invulnerableDuration;
- if (!level->isClientSide) actuallyHurt(source, dmg);
- hurtTime = hurtDuration = 10;
- }
-
- hurtDir = 0;
-
- shared_ptr<Entity> sourceEntity = source->getEntity();
- if (sourceEntity != NULL)
- {
- if (dynamic_pointer_cast<Mob>(sourceEntity) != NULL) {
- setLastHurtByMob(dynamic_pointer_cast<Mob>(sourceEntity));
-
- }
- if (dynamic_pointer_cast<Player>(sourceEntity) != NULL)
- {
- lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME;
- lastHurtByPlayer = dynamic_pointer_cast<Player>(sourceEntity);
- }
- else if (dynamic_pointer_cast<Wolf>(sourceEntity))
- {
- shared_ptr<Wolf> w = dynamic_pointer_cast<Wolf>(sourceEntity);
- if (w->isTame())
- {
- lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME;
- lastHurtByPlayer = nullptr;
- }
- }
- }
-
- if (sound && level->isClientSide)
- {
- return false;
- }
-
- if (sound)
- {
- level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT);
- if (source != DamageSource::drown && source != DamageSource::controlledExplosion) markHurt();
- if (sourceEntity != NULL)
- {
- double xd = sourceEntity->x - x;
- double zd = sourceEntity->z - z;
- while (xd * xd + zd * zd < 0.0001)
- {
- xd = (Math::random() - Math::random()) * 0.01;
- zd = (Math::random() - Math::random()) * 0.01;
- }
- hurtDir = (float) (atan2(zd, xd) * 180 / PI) - yRot;
- knockback(sourceEntity, dmg, xd, zd);
- }
- else
- {
- hurtDir = (float) (int) ((Math::random() * 2) * 180); // 4J This cast is the same as Java
- }
- }
-
- MemSect(31);
- if (health <= 0)
- {
- if (sound) level->playSound(shared_from_this(), getDeathSound(), getSoundVolume(), getVoicePitch());
- die(source);
- }
- else
- {
- if (sound) level->playSound(shared_from_this(), getHurtSound(), getSoundVolume(), getVoicePitch());
- }
- MemSect(0);
-
- return true;
-}
-
-float Mob::getVoicePitch()
-{
- if (isBaby())
- {
- return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.5f;
-
- }
- return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f;
-}
-
-void Mob::animateHurt()
-{
- hurtTime = hurtDuration = 10;
- hurtDir = 0;
-}
-
-int Mob::getArmorValue()
-{
- return 0;
-}
-
-void Mob::hurtArmor(int damage)
-{
-}
-
-int Mob::getDamageAfterArmorAbsorb(DamageSource *damageSource, int damage)
-{
- if (!damageSource->isBypassArmor())
- {
- int absorb = 25 - getArmorValue();
- int v = (damage) * absorb + dmgSpill;
- hurtArmor(damage);
- damage = v / 25;
- dmgSpill = v % 25;
- }
- return damage;
-}
-
-int Mob::getDamageAfterMagicAbsorb(DamageSource *damageSource, int damage)
-{
- if (hasEffect(MobEffect::damageResistance))
- {
- int absorbValue = (getEffect(MobEffect::damageResistance)->getAmplifier() + 1) * 5;
- int absorb = 25 - absorbValue;
- int v = (damage) * absorb + dmgSpill;
- damage = v / 25;
- dmgSpill = v % 25;
+ return LivingEntity::tickHeadTurn(yBodyRotT, walkSpeed);
}
- return damage;
-}
-
-void Mob::actuallyHurt(DamageSource *source, int dmg)
-{
- dmg = getDamageAfterArmorAbsorb(source, dmg);
- dmg = getDamageAfterMagicAbsorb(source, dmg);
- health -= dmg;
-}
-
-
-float Mob::getSoundVolume()
-{
- return 1;
}
int Mob::getAmbientSound()
@@ -824,82 +250,9 @@ int Mob::getAmbientSound()
return -1;
}
-int Mob::getHurtSound()
+int Mob::getDeathLoot()
{
- return eSoundType_DAMAGE_HURT;
-}
-
-int Mob::getDeathSound()
-{
- return eSoundType_DAMAGE_HURT;
-}
-
-void Mob::knockback(shared_ptr<Entity> source, int dmg, double xd, double zd)
-{
- hasImpulse = true;
- float dd = (float) sqrt(xd * xd + zd * zd);
- float pow = 0.4f;
-
- this->xd /= 2;
- this->yd /= 2;
- this->zd /= 2;
-
- this->xd -= xd / dd * pow;
- this->yd += pow;
- this->zd -= zd / dd * pow;
-
- if (this->yd > 0.4f) this->yd = 0.4f;
-}
-
-void Mob::die(DamageSource *source)
-{
- shared_ptr<Entity> sourceEntity = source->getEntity();
- if (deathScore >= 0 && sourceEntity != NULL) sourceEntity->awardKillScore(shared_from_this(), deathScore);
-
- if (sourceEntity != NULL) sourceEntity->killed( dynamic_pointer_cast<Mob>( shared_from_this() ) );
-
- dead = true;
-
- if (!level->isClientSide)
- {
- int playerBonus = 0;
- shared_ptr<Player> player = dynamic_pointer_cast<Player>(sourceEntity);
- if (player != NULL)
- {
- playerBonus = EnchantmentHelper::getKillingLootBonus(player->inventory);
- }
- if (!isBaby())
- {
- dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus);
- if (lastHurtByPlayerTime > 0)
- {
- int rareLoot = random->nextInt(200) - playerBonus;
- if (rareLoot < 5)
- {
- dropRareDeathLoot((rareLoot <= 0) ? 1 : 0);
- }
- }
- }
-
- // 4J-JEV, hook for Durango mobKill event.
- if (player != NULL)
- {
- player->awardStat(GenericStats::killMob(),GenericStats::param_mobKill(player, dynamic_pointer_cast<Mob>(shared_from_this()), source));
- }
- }
-
- level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH);
-}
-
-/**
-* Drop extra rare loot. Only occurs roughly 5% of the time, rareRootLevel
-* is set to 1 (otherwise 0) 1% of the time.
-*
-* @param rareLootLevel
-*/
-void Mob::dropRareDeathLoot(int rareLootLevel)
-{
-
+ return 0;
}
void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel)
@@ -917,268 +270,85 @@ void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel)
}
}
-int Mob::getDeathLoot()
+void Mob::addAdditonalSaveData(CompoundTag *entityTag)
{
- return 0;
-}
+ LivingEntity::addAdditonalSaveData(entityTag);
+ entityTag->putBoolean(L"CanPickUpLoot", canPickUpLoot());
+ entityTag->putBoolean(L"PersistenceRequired", persistenceRequired);
-void Mob::causeFallDamage(float distance)
-{
- Entity::causeFallDamage(distance);
- int dmg = (int) ceil(distance - 3);
- if (dmg > 0)
+ ListTag<CompoundTag> *gear = new ListTag<CompoundTag>();
+ for (int i = 0; i < equipment.length; i++)
{
- // 4J - new sounds here brought forward from 1.2.3
- if (dmg > 4)
- {
- level->playSound(shared_from_this(), eSoundType_DAMAGE_FALL_BIG, 1, 1);
- }
- else
- {
- level->playSound(shared_from_this(), eSoundType_DAMAGE_FALL_SMALL, 1, 1);
- }
- hurt(DamageSource::fall, dmg);
-
- int t = level->getTile( Mth::floor(x), Mth::floor(y - 0.2f - this->heightOffset), Mth::floor(z));
- if (t > 0)
- {
- const Tile::SoundType *soundType = Tile::tiles[t]->soundType;
- MemSect(31);
- level->playSound(shared_from_this(), soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f);
- MemSect(0);
- }
+ CompoundTag *tag = new CompoundTag();
+ if (equipment[i] != NULL) equipment[i]->save(tag);
+ gear->add(tag);
}
-}
+ entityTag->put(L"Equipment", gear);
-void Mob::travel(float xa, float ya)
-{
-#ifdef __PSVITA__
- // AP - dynamic_pointer_cast is a non-trivial call
- Player *thisPlayer = NULL;
- if( (GetType() & eTYPE_PLAYER) == eTYPE_PLAYER )
+ ListTag<FloatTag> *dropChanceList = new ListTag<FloatTag>();
+ for (int i = 0; i < dropChances.length; i++)
{
- thisPlayer = (Player*) this;
+ dropChanceList->add(new FloatTag( _toString(i), dropChances[i]));
}
-#else
- shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
-#endif
- if (isInWater() && !(thisPlayer && thisPlayer->abilities.flying) )
- {
- double yo = y;
- moveRelative(xa, ya, useNewAi() ? 0.04f : 0.02f);
- move(xd, yd, zd);
-
- xd *= 0.80f;
- yd *= 0.80f;
- zd *= 0.80f;
- yd -= 0.02;
+ entityTag->put(L"DropChances", dropChanceList);
+ entityTag->putString(L"CustomName", getCustomName());
+ entityTag->putBoolean(L"CustomNameVisible", isCustomNameVisible());
- if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd))
- {
- yd = 0.3f;
- }
- }
- else if (isInLava() && !(thisPlayer && thisPlayer->abilities.flying) )
+ // leash info
+ entityTag->putBoolean(L"Leashed", _isLeashed);
+ if (leashHolder != NULL)
{
- double yo = y;
- moveRelative(xa, ya, 0.02f);
- move(xd, yd, zd);
- xd *= 0.50f;
- yd *= 0.50f;
- zd *= 0.50f;
- yd -= 0.02;
-
- if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd))
+ CompoundTag *leashTag = new CompoundTag(L"Leash");
+ if ( leashHolder->instanceof(eTYPE_LIVINGENTITY) )
{
- yd = 0.3f;
+ // a walking, talking, leash holder
+ leashTag->putString(L"UUID", leashHolder->getUUID());
}
- }
- else
- {
- float friction = 0.91f;
- if (onGround)
+ else if ( leashHolder->instanceof(eTYPE_HANGING_ENTITY) )
{
- friction = 0.6f * 0.91f;
- int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z));
- if (t > 0)
- {
- friction = Tile::tiles[t]->friction * 0.91f;
- }
+ // a fixed holder (that doesn't save itself)
+ shared_ptr<HangingEntity> hangInThere = dynamic_pointer_cast<HangingEntity>(leashHolder);
+ leashTag->putInt(L"X", hangInThere->xTile);
+ leashTag->putInt(L"Y", hangInThere->yTile);
+ leashTag->putInt(L"Z", hangInThere->zTile);
}
-
- float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction);
-
- float speed;
- if (onGround)
- {
- if (useNewAi()) speed = getSpeed();
- else speed = walkingSpeed;
- speed *= friction2;
- }
- else speed = flyingSpeed;
-
- moveRelative(xa, ya, speed);
-
- friction = 0.91f;
- if (onGround)
- {
- friction = 0.6f * 0.91f;
- int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z));
- if (t > 0)
- {
- friction = Tile::tiles[t]->friction * 0.91f;
- }
- }
- if (onLadder())
- {
- float max = 0.15f;
- if (xd < -max) xd = -max;
- if (xd > max) xd = max;
- if (zd < -max) zd = -max;
- if (zd > max) zd = max;
- this->fallDistance = 0;
- if (yd < -0.15) yd = -0.15;
- bool playerSneaking = isSneaking() && dynamic_pointer_cast<Player>(shared_from_this()) != NULL;
- if (playerSneaking && yd < 0) yd = 0;
- }
-
- move(xd, yd, zd);
-
- if (horizontalCollision && onLadder())
- {
- yd = 0.2;
- }
-
- yd -= 0.08;
- yd *= 0.98f;
- xd *= friction;
- zd *= friction;
+ entityTag->put(L"Leash", leashTag);
}
-
- walkAnimSpeedO = walkAnimSpeed;
- double xxd = x - xo;
- double zzd = z - zo;
- float wst = Mth::sqrt(xxd * xxd + zzd * zzd) * 4;
- if (wst > 1) wst = 1;
- walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f;
- walkAnimPos += walkAnimSpeed;
}
-bool Mob::onLadder()
+void Mob::readAdditionalSaveData(CompoundTag *tag)
{
- int xt = Mth::floor(x);
- int yt = Mth::floor(bb->y0);
- int zt = Mth::floor(z);
-
- // 4J-PB - TU9 - add climbable vines
- int iTile = level->getTile(xt, yt, zt);
- return (iTile== Tile::ladder_Id) || (iTile== Tile::vine_Id);
-}
+ LivingEntity::readAdditionalSaveData(tag);
+ setCanPickUpLoot(tag->getBoolean(L"CanPickUpLoot"));
+ persistenceRequired = tag->getBoolean(L"PersistenceRequired");
+ if (tag->contains(L"CustomName") && tag->getString(L"CustomName").length() > 0) setCustomName(tag->getString(L"CustomName"));
+ setCustomNameVisible(tag->getBoolean(L"CustomNameVisible"));
-bool Mob::isShootable()
-{
- return true;
-}
-
-void Mob::addAdditonalSaveData(CompoundTag *entityTag)
-{
- entityTag->putShort(L"Health", (short) health);
- entityTag->putShort(L"HurtTime", (short) hurtTime);
- entityTag->putShort(L"DeathTime", (short) deathTime);
- entityTag->putShort(L"AttackTime", (short) attackTime);
-
- if (!activeEffects.empty())
+ if (tag->contains(L"Equipment"))
{
- ListTag<CompoundTag> *listTag = new ListTag<CompoundTag>();
+ ListTag<CompoundTag> *gear = (ListTag<CompoundTag> *) tag->getList(L"Equipment");
- for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it)
+ for (int i = 0; i < equipment.length; i++)
{
- MobEffectInstance *effect = it->second;
-
- CompoundTag *tag = new CompoundTag();
- tag->putByte(L"Id", (BYTE) effect->getId());
- tag->putByte(L"Amplifier", (char) effect->getAmplifier());
- tag->putInt(L"Duration", effect->getDuration());
- listTag->add(tag);
+ equipment[i] = ItemInstance::fromTag(gear->get(i));
}
- entityTag->put(L"ActiveEffects", listTag);
}
-}
-
-void Mob::readAdditionalSaveData(CompoundTag *tag)
-{
- if (health < Short::MIN_VALUE) health = Short::MIN_VALUE;
- health = tag->getShort(L"Health");
- if (!tag->contains(L"Health")) health = getMaxHealth();
- hurtTime = tag->getShort(L"HurtTime");
- deathTime = tag->getShort(L"DeathTime");
- attackTime = tag->getShort(L"AttackTime");
- if (tag->contains(L"ActiveEffects"))
+ if (tag->contains(L"DropChances"))
{
- ListTag<CompoundTag> *effects = (ListTag<CompoundTag> *) tag->getList(L"ActiveEffects");
- for (int i = 0; i < effects->size(); i++)
+ ListTag<FloatTag> *items = (ListTag<FloatTag> *) tag->getList(L"DropChances");
+ for (int i = 0; i < items->size(); i++)
{
- CompoundTag *effectTag = effects->get(i);
- int id = effectTag->getByte(L"Id");
- int amplifier = effectTag->getByte(L"Amplifier");
- int duration = effectTag->getInt(L"Duration");
-
- activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( id, new MobEffectInstance(id, duration, amplifier) ) );
+ dropChances[i] = items->get(i)->data;
}
}
-}
-bool Mob::isAlive()
-{
- return !removed && health > 0;
-}
-
-bool Mob::isWaterMob()
-{
- return false;
-}
-
-// 4J - added for more accurate lighting of mobs. Takes a weighted average of all tiles touched by the bounding volume of the entity - the method in the Entity class (which used to be used for
-// mobs too) simply gets a single tile's lighting value causing sudden changes of lighting values when entities go in and out of lit areas, for example when bobbing in the water.
-int Mob::getLightColor(float a)
-{
- float accum[2] = {0,0};
- float totVol = ( bb->x1 - bb->x0 ) * ( bb->y1 - bb->y0 ) * ( bb->z1 - bb->z0 );
- int xmin = Mth::floor(bb->x0);
- int xmax = Mth::floor(bb->x1);
- int ymin = Mth::floor(bb->y0);
- int ymax = Mth::floor(bb->y1);
- int zmin = Mth::floor(bb->z0);
- int zmax = Mth::floor(bb->z1);
- for( int xt = xmin; xt <= xmax; xt++ )
- for( int yt = ymin; yt <= ymax; yt++ )
- for( int zt = zmin; zt <= zmax; zt++ )
- {
- float tilexmin = (float)xt;
- float tilexmax = (float)(xt+1);
- float tileymin = (float)yt;
- float tileymax = (float)(yt+1);
- float tilezmin = (float)zt;
- float tilezmax = (float)(zt+1);
- if( tilexmin < bb->x0 ) tilexmin = bb->x0;
- if( tilexmax > bb->x1 ) tilexmax = bb->x1;
- if( tileymin < bb->y0 ) tileymin = bb->y0;
- if( tileymax > bb->y1 ) tileymax = bb->y1;
- if( tilezmin < bb->z0 ) tilezmin = bb->z0;
- if( tilezmax > bb->z1 ) tilezmax = bb->z1;
- float tileVol = ( tilexmax - tilexmin ) * ( tileymax - tileymin ) * ( tilezmax - tilezmin );
- float frac = tileVol / totVol;
- int lc = level->getLightColor(xt, yt, zt, 0);
- accum[0] += frac * (float)( lc & 0xffff );
- accum[1] += frac * (float)( lc >> 16 );
- }
-
- if( accum[0] > 240.0f ) accum[0] = 240.0f;
- if( accum[1] > 240.0f ) accum[1] = 240.0f;
-
- return ( ( (int)accum[1])<<16) | ((int)accum[0]);
+ _isLeashed = tag->getBoolean(L"Leashed");
+ if (_isLeashed && tag->contains(L"Leash"))
+ {
+ leashInfoTag = (CompoundTag *)tag->getCompound(L"Leash")->copy();
+ }
}
void Mob::setYya(float yya)
@@ -1186,123 +356,99 @@ void Mob::setYya(float yya)
this->yya = yya;
}
-void Mob::setJumping(bool jump)
+void Mob::setSpeed(float speed)
{
- jumping = jump;
+ LivingEntity::setSpeed(speed);
+ setYya(speed);
}
void Mob::aiStep()
{
- if (noJumpDelay > 0) noJumpDelay--;
- if (lSteps > 0)
- {
- double xt = x + (lx - x) / lSteps;
- double yt = y + (ly - y) / lSteps;
- double zt = z + (lz - z) / lSteps;
-
- double yrd = Mth::wrapDegrees(lyr - yRot);
- double xrd = Mth::wrapDegrees(lxr - xRot);
-
- yRot += (float) ( (yrd) / lSteps );
- xRot += (float) ( (xrd) / lSteps );
-
- lSteps--;
- this->setPos(xt, yt, zt);
- this->setRot(yRot, xRot);
-
- // 4J - this collision is carried out to try and stop the lerping push the mob through the floor,
- // in which case gravity can then carry on moving the mob because the collision just won't work anymore.
- // BB for collision used to be calculated as: bb->shrink(1 / 32.0, 0, 1 / 32.0)
- // now using a reduced BB to try and get rid of some issues where mobs pop up the sides of walls, undersides of
- // trees etc.
- AABB *shrinkbb = bb->shrink(0.1, 0, 0.1);
- shrinkbb->y1 = shrinkbb->y0 + 0.1;
- AABBList *collisions = level->getCubes(shared_from_this(), shrinkbb);
- if (collisions->size() > 0)
- {
- double yTop = 0;
- AUTO_VAR(itEnd, collisions->end());
- for (AUTO_VAR(it, collisions->begin()); it != itEnd; it++)
- {
- AABB *ab = *it; //collisions->at(i);
- if (ab->y1 > yTop) yTop = ab->y1;
- }
-
- yt += yTop - bb->y0;
- setPos(xt, yt, zt);
- }
- if (abs(xd) < MIN_MOVEMENT_DISTANCE) xd = 0;
- if (abs(yd) < MIN_MOVEMENT_DISTANCE) yd = 0;
- if (abs(zd) < MIN_MOVEMENT_DISTANCE) zd = 0;
- }
+ LivingEntity::aiStep();
- if (isImmobile())
- {
- jumping = false;
- xxa = 0;
- yya = 0;
- yRotA = 0;
- }
- else
+ if (!level->isClientSide && canPickUpLoot() && !dead && level->getGameRules()->getBoolean(GameRules::RULE_MOBGRIEFING))
{
- MemSect(25);
- if (isEffectiveAI())
+ vector<shared_ptr<Entity> > *entities = level->getEntitiesOfClass(typeid(ItemEntity), bb->grow(1, 0, 1));
+ for (AUTO_VAR(it, entities->begin()); it != entities->end(); ++it)
{
- if (useNewAi())
- {
- newServerAiStep();
- }
- else
- {
- serverAiStep();
- yHeadRot = yRot;
- }
- }
- MemSect(0);
- }
+ shared_ptr<ItemEntity> entity = dynamic_pointer_cast<ItemEntity>(*it);
+ if (entity->removed || entity->getItem() == NULL) continue;
+ shared_ptr<ItemInstance> item = entity->getItem();
+ int slot = getEquipmentSlotForItem(item);
- if (jumping)
- {
- if (isInWater() || isInLava() )
- {
- yd += 0.04f;
- }
- else if (onGround)
- {
- if (noJumpDelay == 0)
+ if (slot > -1)
{
- jumpFromGround();
- noJumpDelay = 10;
- }
- }
- }
- else
- {
- noJumpDelay = 0;
- }
-
+ bool replace = true;
+ shared_ptr<ItemInstance> current = getCarried(slot);
- xxa *= 0.98f;
- yya *= 0.98f;
- yRotA *= 0.9f;
-
- float normalSpeed = walkingSpeed;
- walkingSpeed *= getWalkingSpeedModifier();
- travel(xxa, yya);
- walkingSpeed = normalSpeed;
+ if (current != NULL)
+ {
+ if (slot == SLOT_WEAPON)
+ {
+ WeaponItem *newWeapon = dynamic_cast<WeaponItem *>(item->getItem());
+ WeaponItem *oldWeapon = dynamic_cast<WeaponItem *>(current->getItem());
+ if ( newWeapon != NULL && oldWeapon == NULL)
+ {
+ replace = true;
+ }
+ else if (newWeapon != NULL && oldWeapon != NULL)
+ {
+ if (newWeapon->getTierDamage() == oldWeapon->getTierDamage())
+ {
+ replace = item->getAuxValue() > current->getAuxValue() || item->hasTag() && !current->hasTag();
+ }
+ else
+ {
+ replace = newWeapon->getTierDamage() > oldWeapon->getTierDamage();
+ }
+ }
+ else
+ {
+ replace = false;
+ }
+ }
+ else
+ {
+ ArmorItem *newArmor = dynamic_cast<ArmorItem *>(item->getItem());
+ ArmorItem *oldArmor = dynamic_cast<ArmorItem *>(current->getItem());
+ if (newArmor != NULL && oldArmor == NULL)
+ {
+ replace = true;
+ }
+ else if (newArmor != NULL && oldArmor != NULL)
+ {
+ if (newArmor->defense == oldArmor->defense)
+ {
+ replace = item->getAuxValue() > current->getAuxValue() || item->hasTag() && !current->hasTag();
+ }
+ else
+ {
+ replace = newArmor->defense > oldArmor->defense;
+ }
+ }
+ else
+ {
+ replace = false;
+ }
+ }
+ }
- if(!level->isClientSide)
- {
- vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f));
- if (entities != NULL && !entities->empty())
- {
- AUTO_VAR(itEnd, entities->end());
- for (AUTO_VAR(it, entities->begin()); it != itEnd; it++)
- {
- shared_ptr<Entity> e = *it; //entities->at(i);
- if (e->isPushable()) e->push(shared_from_this());
+ if (replace)
+ {
+ if (current != NULL && random->nextFloat() - 0.1f < dropChances[slot])
+ {
+ spawnAtLocation(current, 0);
+ }
+
+ setEquippedSlot(slot, item);
+ dropChances[slot] = 2;
+ persistenceRequired = true;
+ take(entity, 1);
+ entity->remove();
+ }
}
}
+ delete entities;
}
}
@@ -1311,38 +457,6 @@ bool Mob::useNewAi()
return false;
}
-bool Mob::isEffectiveAI()
-{
- return !level->isClientSide;
-}
-
-bool Mob::isImmobile()
-{
- return health <= 0;
-}
-
-bool Mob::isBlocking()
-{
- return false;
-}
-
-void Mob::jumpFromGround()
-{
- yd = 0.42f;
- if (hasEffect(MobEffect::jump))
- {
- yd += (getEffect(MobEffect::jump)->getAmplifier() + 1) * .1f;
- }
- if (isSprinting())
- {
- float rr = yRot * Mth::RAD_TO_GRAD;
-
- xd -= Mth::sin(rr) * 0.2f;
- zd += Mth::cos(rr) * 0.2f;
- }
- this->hasImpulse = true;
-}
-
bool Mob::removeWhenFarAway()
{
return true;
@@ -1350,6 +464,11 @@ bool Mob::removeWhenFarAway()
void Mob::checkDespawn()
{
+ if (persistenceRequired)
+ {
+ noActionTime = 0;
+ return;
+ }
shared_ptr<Entity> player = level->getNearestPlayer(shared_from_this(), -1);
if (player != NULL)
{
@@ -1376,36 +495,54 @@ void Mob::checkDespawn()
void Mob::newServerAiStep()
{
+ PIXBeginNamedEvent(0,"Tick target selector for %d",GetType());
MemSect(51);
noActionTime++;
+ PIXBeginNamedEvent(0,"Check despawn");
checkDespawn();
- sensing->tick();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick sensing");
+ sensing->tick();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick target selector");
targetSelector.tick();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick goal selectors");
goalSelector.tick();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick navigation");
navigation->tick();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick server ai mob step");
serverAiMobStep();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick move");
moveControl->tick();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick look");
lookControl->tick();
+ PIXEndNamedEvent();
+ PIXBeginNamedEvent(0,"Tick jump");
jumpControl->tick();
+ PIXEndNamedEvent();
// 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
// extra wandering is to potentially transition from protected to not protected.
+ PIXBeginNamedEvent(0,"Consider extra wandering");
considerForExtraWandering( isDespawnProtected() );
+ PIXEndNamedEvent();
MemSect(0);
-}
-
-void Mob::serverAiMobStep()
-{
+ PIXEndNamedEvent();
}
void Mob::serverAiStep()
{
- noActionTime++;
-
- checkDespawn();
+ LivingEntity::serverAiStep();
xxa = 0;
yya = 0;
+ checkDespawn();
+
float lookDistance = 8;
if (random->nextFloat() < 0.02f)
{
@@ -1454,11 +591,12 @@ void Mob::lookAt(shared_ptr<Entity> e, float yMax, float xMax)
double xd = e->x - x;
double yd;
double zd = e->z - z;
+
- shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(e);
- if(mob != NULL)
+ if ( e->instanceof(eTYPE_LIVINGENTITY) )
{
- yd = (y + getHeadHeight()) - (mob->y + mob->getHeadHeight());
+ shared_ptr<LivingEntity> mob = dynamic_pointer_cast<LivingEntity>(e);
+ yd = (mob->y + mob->getHeadHeight()) - (y + getHeadHeight());
}
else
{
@@ -1469,7 +607,7 @@ void Mob::lookAt(shared_ptr<Entity> e, float yMax, float xMax)
float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90;
float xRotD = (float) -(atan2(yd, sd) * 180 / PI);
- xRot = -rotlerp(xRot, xRotD, xMax);
+ xRot = rotlerp(xRot, xRotD, xMax);
yRot = rotlerp(yRot, yRotD, yMax);
}
@@ -1503,59 +641,6 @@ bool Mob::canSpawn()
return level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid_NoLoad(bb);
}
-void Mob::outOfWorld()
-{
- hurt(DamageSource::outOfWorld, 4);
-}
-
-float Mob::getAttackAnim(float a)
-{
- float diff = attackAnim - oAttackAnim;
- if (diff < 0) diff += 1;
- return oAttackAnim + diff * a;
-}
-
-
-Vec3 *Mob::getPos(float a)
-{
- if (a == 1)
- {
- return Vec3::newTemp(x, y, z);
- }
- double x = xo + (this->x - xo) * a;
- double y = yo + (this->y - yo) * a;
- double z = zo + (this->z - zo) * a;
-
- return Vec3::newTemp(x, y, z);
-}
-
-Vec3 *Mob::getLookAngle()
-{
- return getViewVector(1);
-}
-
-Vec3 *Mob::getViewVector(float a)
-{
- if (a == 1)
- {
- float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI);
- float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI);
- float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD);
- float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD);
-
- return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos);
- }
- float xRot = xRotO + (this->xRot - xRotO) * a;
- float yRot = yRotO + (this->yRot - yRotO) * a;
-
- float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI);
- float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI);
- float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD);
- float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD);
-
- return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos);
-}
-
float Mob::getSizeScale()
{
return 1.0f;
@@ -1566,362 +651,421 @@ float Mob::getHeadSizeScale()
return 1.0f;
}
-HitResult *Mob::pick(double range, float a)
-{
- Vec3 *from = getPos(a);
- Vec3 *b = getViewVector(a);
- Vec3 *to = from->add(b->x * range, b->y * range, b->z * range);
- return level->clip(from, to);
-}
-
int Mob::getMaxSpawnClusterSize()
{
return 4;
}
-shared_ptr<ItemInstance> Mob::getCarriedItem()
+int Mob::getMaxFallDistance()
{
- return nullptr;
+ if (getTarget() == NULL) return 3;
+ int sacrifice = (int) (getHealth() - (getMaxHealth() * 0.33f));
+ sacrifice -= (3 - level->difficulty) * 4;
+ if (sacrifice < 0) sacrifice = 0;
+ return sacrifice + 3;
}
-shared_ptr<ItemInstance> Mob::getArmor(int pos)
+shared_ptr<ItemInstance> Mob::getCarriedItem()
{
- // 4J Stu - Not implemented yet
- return nullptr;
- //return equipment[pos + 1];
+ return equipment[SLOT_WEAPON];
}
-void Mob::handleEntityEvent(byte id)
+shared_ptr<ItemInstance> Mob::getCarried(int slot)
{
- if (id == EntityEvent::HURT)
- {
- this->walkAnimSpeed = 1.5f;
-
- invulnerableTime = invulnerableDuration;
- hurtTime = hurtDuration = 10;
- hurtDir = 0;
-
- MemSect(31);
- // 4J-PB -added because villagers have no sounds
- int iHurtSound=getHurtSound();
- if(iHurtSound!=-1)
- {
- level->playSound(shared_from_this(), iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
- }
- MemSect(0);
- hurt(DamageSource::genericSource, 0);
- }
- else if (id == EntityEvent::DEATH)
- {
- MemSect(31);
- // 4J-PB -added because villagers have no sounds
- int iDeathSound=getDeathSound();
- if(iDeathSound!=-1)
- {
- level->playSound(shared_from_this(), iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);
- }
- MemSect(0);
- health = 0;
- die(DamageSource::genericSource);
- }
- else
- {
- Entity::handleEntityEvent(id);
- }
+ return equipment[slot];
}
-bool Mob::isSleeping()
+shared_ptr<ItemInstance> Mob::getArmor(int pos)
{
- return false;
+ return equipment[pos + 1];
}
-Icon *Mob::getItemInHandIcon(shared_ptr<ItemInstance> item, int layer)
+void Mob::setEquippedSlot(int slot, shared_ptr<ItemInstance> item)
{
- return item->getIcon();
+ equipment[slot] = item;
}
-// 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.
-bool Mob::shouldRender(Vec3 *c)
+ItemInstanceArray Mob::getEquipmentSlots()
{
- 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)))
- {
- return false;
- }
- return Entity::shouldRender(c);
+ return equipment;
}
-void Mob::tickEffects()
+void Mob::dropEquipment(bool byPlayer, int playerBonusLevel)
{
- bool removed = false;
- for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();)
+ for (int slot = 0; slot < getEquipmentSlots().length; slot++)
{
- MobEffectInstance *effect = it->second;
- removed = false;
- if (!effect->tick(dynamic_pointer_cast<Mob>(shared_from_this())))
+ shared_ptr<ItemInstance> item = getCarried(slot);
+ bool preserve = dropChances[slot] > 1;
+
+ if (item != NULL && (byPlayer || preserve) && random->nextFloat() - playerBonusLevel * 0.01f < dropChances[slot])
{
- if (!level->isClientSide)
+ if (!preserve && item->isDamageableItem())
{
- it = activeEffects.erase( it );
- onEffectRemoved(effect);
- delete effect;
- removed = true;
+ int _max = max(item->getMaxDamage() - 25, 1);
+ int damage = item->getMaxDamage() - random->nextInt(random->nextInt(_max) + 1);
+ if (damage > _max) damage = _max;
+ if (damage < 1) damage = 1;
+ item->setAuxValue(damage);
}
- }
- if(!removed)
- {
- ++it;
+ spawnAtLocation(item, 0);
}
}
- if (effectsDirty)
+}
+
+void Mob::populateDefaultEquipmentSlots()
+{
+ if (random->nextFloat() < MAX_WEARING_ARMOR_CHANCE * level->getDifficulty(x, y, z))
{
- if (!level->isClientSide)
+ int armorType = random->nextInt(2);
+ float partialChance = level->difficulty == Difficulty::HARD ? 0.1f : 0.25f;
+ if (random->nextFloat() < 0.095f) armorType++;
+ if (random->nextFloat() < 0.095f) armorType++;
+ if (random->nextFloat() < 0.095f) armorType++;
+
+ for (int i = 3; i >= 0; i--)
{
- if (activeEffects.empty())
+ shared_ptr<ItemInstance> item = getArmor(i);
+ if (i < 3 && random->nextFloat() < partialChance) break;
+ if (item == NULL)
{
- entityData->set(DATA_EFFECT_COLOR_ID, (int) 0);
- setInvisible(false);
- setWeakened(false);
- }
- else
- {
- vector<MobEffectInstance *> values;
- for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();++it)
- {
- values.push_back(it->second);
- }
- int colorValue = PotionBrewing::getColorValue(&values);
- values.clear();
- entityData->set(DATA_EFFECT_COLOR_ID, colorValue);
- setInvisible(hasEffect(MobEffect::invisibility->id));
- setWeakened(hasEffect(MobEffect::weakness->id));
+ Item *equip = getEquipmentForSlot(i + 1, armorType);
+ if (equip != NULL) setEquippedSlot(i + 1, shared_ptr<ItemInstance>(new ItemInstance(equip)));
}
}
- effectsDirty = false;
}
- if (random->nextBoolean())
+}
+
+int Mob::getEquipmentSlotForItem(shared_ptr<ItemInstance> item)
+{
+ if (item->id == Tile::pumpkin_Id || item->id == Item::skull_Id)
{
- int colorValue = entityData->getInteger(DATA_EFFECT_COLOR_ID);
- if (colorValue > 0)
- {
- double red = (double) ((colorValue >> 16) & 0xff) / 255.0;
- double green = (double) ((colorValue >> 8) & 0xff) / 255.0;
- double blue = (double) ((colorValue >> 0) & 0xff) / 255.0;
+ return SLOT_HELM;
+ }
- level->addParticle(eParticleType_mobSpell, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - heightOffset, z + (random->nextDouble() - 0.5) * bbWidth, red, green, blue);
+ ArmorItem *armorItem = dynamic_cast<ArmorItem *>(item->getItem());
+ if (armorItem != NULL)
+ {
+ switch (armorItem->slot)
+ {
+ case ArmorItem::SLOT_FEET:
+ return SLOT_BOOTS;
+ case ArmorItem::SLOT_LEGS:
+ return SLOT_LEGGINGS;
+ case ArmorItem::SLOT_TORSO:
+ return SLOT_CHEST;
+ case ArmorItem::SLOT_HEAD:
+ return SLOT_HELM;
}
}
+
+ return SLOT_WEAPON;
}
-void Mob::removeAllEffects()
+Item *Mob::getEquipmentForSlot(int slot, int type)
{
- //Iterator<Integer> effectIdIterator = activeEffects.keySet().iterator();
- //while (effectIdIterator.hasNext())
- for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); )
+ switch (slot)
{
- //Integer effectId = effectIdIterator.next();
- MobEffectInstance *effect = it->second;//activeEffects.get(effectId);
+ case SLOT_HELM:
+ if (type == 0) return Item::helmet_leather;
+ if (type == 1) return Item::helmet_gold;
+ if (type == 2) return Item::helmet_chain;
+ if (type == 3) return Item::helmet_iron;
+ if (type == 4) return Item::helmet_diamond;
+ case SLOT_CHEST:
+ if (type == 0) return Item::chestplate_leather;
+ if (type == 1) return Item::chestplate_gold;
+ if (type == 2) return Item::chestplate_chain;
+ if (type == 3) return Item::chestplate_iron;
+ if (type == 4) return Item::chestplate_diamond;
+ case SLOT_LEGGINGS:
+ if (type == 0) return Item::leggings_leather;
+ if (type == 1) return Item::leggings_gold;
+ if (type == 2) return Item::leggings_chain;
+ if (type == 3) return Item::leggings_iron;
+ if (type == 4) return Item::leggings_diamond;
+ case SLOT_BOOTS:
+ if (type == 0) return Item::boots_leather;
+ if (type == 1) return Item::boots_gold;
+ if (type == 2) return Item::boots_chain;
+ if (type == 3) return Item::boots_iron;
+ if (type == 4) return Item::boots_diamond;
+ }
- if (!level->isClientSide)
- {
- //effectIdIterator.remove();
- it = activeEffects.erase(it);
- onEffectRemoved(effect);
- delete effect;
- }
- else
+ return NULL;
+}
+
+void Mob::populateDefaultEquipmentEnchantments()
+{
+ float difficulty = level->getDifficulty(x, y, z);
+
+ if (getCarriedItem() != NULL && random->nextFloat() < MAX_ENCHANTED_WEAPON_CHANCE * difficulty) {
+ EnchantmentHelper::enchantItem(random, getCarriedItem(), (int) (5 + difficulty * random->nextInt(18)));
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ shared_ptr<ItemInstance> item = getArmor(i);
+ if (item != NULL && random->nextFloat() < MAX_ENCHANTED_ARMOR_CHANCE * difficulty)
{
- ++it;
+ EnchantmentHelper::enchantItem(random, item, (int) (5 + difficulty * random->nextInt(18)));
}
}
}
-vector<MobEffectInstance *> *Mob::getActiveEffects()
+/**
+* Added this method so mobs can handle their own spawn settings instead of
+* hacking MobSpawner.java
+*
+* @param groupData
+* TODO
+* @return TODO
+*/
+MobGroupData *Mob::finalizeMobSpawn(MobGroupData *groupData, int extraData /*= 0*/) // 4J Added extraData param
{
- vector<MobEffectInstance *> *active = new vector<MobEffectInstance *>();
+ // 4J Stu - Take this out, it's not great and nobody will notice. Also not great for performance.
+ //getAttribute(SharedMonsterAttributes::FOLLOW_RANGE)->addModifier(new AttributeModifier(random->nextGaussian() * 0.05, AttributeModifier::OPERATION_MULTIPLY_BASE));
- for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it)
- {
- active->push_back(it->second);
- }
+ return groupData;
+}
- return active;
+void Mob::finalizeSpawnEggSpawn(int extraData)
+{
}
-bool Mob::hasEffect(int id)
+bool Mob::canBeControlledByRider()
{
- return activeEffects.find(id) != activeEffects.end();;
+ return false;
}
-bool Mob::hasEffect(MobEffect *effect)
+wstring Mob::getAName()
{
- return activeEffects.find(effect->id) != activeEffects.end();
+ if (hasCustomName()) return getCustomName();
+ return LivingEntity::getAName();
}
-MobEffectInstance *Mob::getEffect(MobEffect *effect)
+void Mob::setPersistenceRequired()
{
- MobEffectInstance *effectInst = NULL;
+ persistenceRequired = true;
+}
- AUTO_VAR(it, activeEffects.find(effect->id));
- if(it != activeEffects.end() ) effectInst = it->second;
+void Mob::setCustomName(const wstring &name)
+{
+ entityData->set(DATA_CUSTOM_NAME, name);
+}
- return effectInst;
+wstring Mob::getCustomName()
+{
+ return entityData->getString(DATA_CUSTOM_NAME);
}
-void Mob::addEffect(MobEffectInstance *newEffect)
+bool Mob::hasCustomName()
{
- if (!canBeAffected(newEffect))
- {
- return;
- }
+ return entityData->getString(DATA_CUSTOM_NAME).length() > 0;
+}
- if (activeEffects.find(newEffect->getId()) != activeEffects.end() )
- {
- // replace effect and update
- MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second;
- effectInst->update(newEffect);
- onEffectUpdated(effectInst);
- }
- else
- {
- activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( newEffect->getId(), newEffect ) );
- onEffectAdded(newEffect);
- }
+void Mob::setCustomNameVisible(bool visible)
+{
+ entityData->set(DATA_CUSTOM_NAME_VISIBLE, visible ? (byte) 1 : (byte) 0);
}
-// 4J Added
-void Mob::addEffectNoUpdate(MobEffectInstance *newEffect)
+bool Mob::isCustomNameVisible()
{
- if (!canBeAffected(newEffect))
- {
- return;
- }
+ return entityData->getByte(DATA_CUSTOM_NAME_VISIBLE) == 1;
+}
- if (activeEffects.find(newEffect->getId()) != activeEffects.end() )
- {
- // replace effect and update
- MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second;
- effectInst->update(newEffect);
- }
- else
- {
- activeEffects.insert( unordered_map<int, MobEffectInstance *>::value_type( newEffect->getId(), newEffect ) );
- }
+bool Mob::shouldShowName()
+{
+ return isCustomNameVisible();
}
-bool Mob::canBeAffected(MobEffectInstance *newEffect)
+void Mob::setDropChance(int slot, float pct)
{
- if (getMobType() == UNDEAD)
- {
- int id = newEffect->getId();
- if (id == MobEffect::regeneration->id || id == MobEffect::poison->id)
- {
- return false;
- }
- }
+ dropChances[slot] = pct;
+}
- return true;
+bool Mob::canPickUpLoot()
+{
+ return _canPickUpLoot;
}
-bool Mob::isInvertedHealAndHarm()
+void Mob::setCanPickUpLoot(bool canPickUpLoot)
{
- return getMobType() == UNDEAD;
+ _canPickUpLoot = canPickUpLoot;
}
-void Mob::removeEffectNoUpdate(int effectId)
+bool Mob::isPersistenceRequired()
{
- AUTO_VAR(it, activeEffects.find(effectId));
- if (it != activeEffects.end())
- {
- MobEffectInstance *effect = it->second;
- if(effect != NULL)
- {
- delete effect;
- }
- activeEffects.erase(it);
- }
+ return persistenceRequired;
}
-void Mob::removeEffect(int effectId)
+bool Mob::interact(shared_ptr<Player> player)
{
- AUTO_VAR(it, activeEffects.find(effectId));
- if (it != activeEffects.end())
+
+ if (isLeashed() && getLeashHolder() == player)
+ {
+ dropLeash(true, !player->abilities.instabuild);
+ return true;
+ }
+
+ shared_ptr<ItemInstance> itemstack = player->inventory->getSelected();
+ if (itemstack != NULL)
{
- MobEffectInstance *effect = it->second;
- if(effect != NULL)
+ // it's inconvenient to have the leash code here, but it's because
+ // the mob.interact(player) method has priority over
+ // item.interact(mob)
+ if (itemstack->id == Item::lead_Id)
{
- onEffectRemoved(effect);
- delete effect;
+ if (canBeLeashed())
+ {
+ shared_ptr<TamableAnimal> tamableAnimal = nullptr;
+ if ( shared_from_this()->instanceof(eTYPE_TAMABLE_ANIMAL)
+ && (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.
+ {
+ if (player->getUUID().compare(tamableAnimal->getOwnerUUID()) == 0)
+ {
+ setLeashedTo(player, true);
+ itemstack->count--;
+ return true;
+ }
+ }
+ else
+ {
+ setLeashedTo(player, true);
+ itemstack->count--;
+ return true;
+ }
+ }
}
- activeEffects.erase(it);
}
-}
-void Mob::onEffectAdded(MobEffectInstance *effect)
-{
- effectsDirty = true;
-}
+ if (mobInteract(player))
+ {
+ return true;
+ }
-void Mob::onEffectUpdated(MobEffectInstance *effect)
-{
- effectsDirty = true;
+ return LivingEntity::interact(player);
}
-void Mob::onEffectRemoved(MobEffectInstance *effect)
+bool Mob::mobInteract(shared_ptr<Player> player)
{
- effectsDirty = true;
+ return false;
}
-float Mob::getWalkingSpeedModifier()
+void Mob::tickLeash()
{
- float speed = 1.0f;
- if (hasEffect(MobEffect::movementSpeed))
+ if (leashInfoTag != NULL)
{
- speed *= 1.0f + .2f * (getEffect(MobEffect::movementSpeed)->getAmplifier() + 1);
+ restoreLeashFromSave();
}
- if (hasEffect(MobEffect::movementSlowdown))
+ if (!_isLeashed)
{
- speed *= 1.0f - .15f * (getEffect(MobEffect::movementSlowdown)->getAmplifier() + 1);
+ return;
+ }
+
+ if (leashHolder == NULL || leashHolder->removed)
+ {
+ dropLeash(true, true);
+ return;
}
- return speed;
}
-void Mob::teleportTo(double x, double y, double z)
+void Mob::dropLeash(bool synch, bool createItemDrop)
{
- moveTo(x, y, z, yRot, xRot);
+ if (_isLeashed)
+ {
+ _isLeashed = false;
+ leashHolder = nullptr;
+ if (!level->isClientSide && createItemDrop)
+ {
+ spawnAtLocation(Item::lead_Id, 1);
+ }
+
+ ServerLevel *serverLevel = dynamic_cast<ServerLevel *>(level);
+ if (!level->isClientSide && synch && serverLevel != NULL)
+ {
+ serverLevel->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEntityLinkPacket>(new SetEntityLinkPacket(SetEntityLinkPacket::LEASH, shared_from_this(), nullptr)));
+ }
+ }
}
-bool Mob::isBaby()
+bool Mob::canBeLeashed()
{
- return false;
+ return !isLeashed() && !shared_from_this()->instanceof(eTYPE_ENEMY);
}
-MobType Mob::getMobType()
+bool Mob::isLeashed()
{
- return UNDEFINED;
+ return _isLeashed;
}
-void Mob::breakItem(shared_ptr<ItemInstance> itemInstance)
+shared_ptr<Entity> Mob::getLeashHolder()
{
- level->playSound(shared_from_this(), eSoundType_RANDOM_BREAK, 0.8f, 0.8f + level->random->nextFloat() * 0.4f);
+ return leashHolder;
+}
- for (int i = 0; i < 5; i++)
+void Mob::setLeashedTo(shared_ptr<Entity> holder, bool synch)
+{
+ _isLeashed = true;
+ leashHolder = holder;
+
+ ServerLevel *serverLevel = dynamic_cast<ServerLevel *>(level);
+ if (!level->isClientSide && synch && serverLevel)
{
- Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0);
- d->xRot(-xRot * PI / 180);
- d->yRot(-yRot * PI / 180);
-
- Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6);
- p->xRot(-xRot * PI / 180);
- p->yRot(-yRot * PI / 180);
- p = p->add(x, y + getHeadHeight(), z);
- level->addParticle(PARTICLE_ICONCRACK(itemInstance->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z);
+ serverLevel->getTracker()->broadcast(shared_from_this(), shared_ptr<SetEntityLinkPacket>( new SetEntityLinkPacket(SetEntityLinkPacket::LEASH, shared_from_this(), leashHolder)));
}
}
-bool Mob::isInvulnerable()
+void Mob::restoreLeashFromSave()
{
- // 4J-JEV: I have no idea what was going on here (it gets changed in a later java version).
- return invulnerableTime > 0; // invulnerableTime <= invulnerableTime / 2;
+ // after being added to the world, attempt to recreate leash bond
+ if (_isLeashed && leashInfoTag != NULL)
+ {
+ if (leashInfoTag->contains(L"UUID"))
+ {
+ wstring leashUuid = leashInfoTag->getString(L"UUID");
+ vector<shared_ptr<Entity> > *livingEnts = level->getEntitiesOfClass(typeid(LivingEntity), bb->grow(10, 10, 10));
+ for(AUTO_VAR(it, livingEnts->begin()); it != livingEnts->end(); ++it)
+ {
+ shared_ptr<LivingEntity> le = dynamic_pointer_cast<LivingEntity>(*it);
+ if (le->getUUID().compare(leashUuid) == 0)
+ {
+ leashHolder = le;
+ setLeashedTo(leashHolder, true);
+ break;
+ }
+ }
+ delete livingEnts;
+ }
+ else if (leashInfoTag->contains(L"X") && leashInfoTag->contains(L"Y") && leashInfoTag->contains(L"Z"))
+ {
+ int x = leashInfoTag->getInt(L"X");
+ int y = leashInfoTag->getInt(L"Y");
+ int z = leashInfoTag->getInt(L"Z");
+
+ shared_ptr<LeashFenceKnotEntity> activeKnot = LeashFenceKnotEntity::findKnotAt(level, x, y, z);
+ if (activeKnot == NULL)
+ {
+ activeKnot = LeashFenceKnotEntity::createAndAddKnot(level, x, y, z);
+ }
+ leashHolder = activeKnot;
+ setLeashedTo(leashHolder, true);
+ }
+ else
+ {
+ dropLeash(false, true);
+ }
+ }
+ leashInfoTag = NULL;
+}
+
+// 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.
+bool Mob::shouldRender(Vec3 *c)
+{
+ 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)))
+ {
+ return false;
+ }
+ return Entity::shouldRender(c);
}
void Mob::setLevel(Level *level)
@@ -1930,14 +1074,4 @@ void Mob::setLevel(Level *level)
navigation->setLevel(level);
goalSelector.setLevel(level);
targetSelector.setLevel(level);
-}
-
-void Mob::finalizeMobSpawn()
-{
-
-}
-
-bool Mob::canBeControlledByRider()
-{
- return false;
-}
+} \ No newline at end of file