2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "JavaMath.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.control.h"
# include "net.minecraft.world.entity.ai.navigation.h"
# include "net.minecraft.world.entity.ai.sensing.h"
# include "net.minecraft.world.entity.player.h"
# include "net.minecraft.world.entity.animal.h"
# include "net.minecraft.world.entity.monster.h"
# include "net.minecraft.world.item.h"
# include "net.minecraft.world.level.h"
# include "net.minecraft.world.level.material.h"
# include "net.minecraft.world.damagesource.h"
# include "net.minecraft.world.effect.h"
# include "net.minecraft.world.item.alchemy.h"
# include "net.minecraft.world.item.enchantment.h"
# include "com.mojang.nbt.h"
# include "Mob.h"
# include "..\Minecraft.Client\Textures.h"
# include "SoundTypes.h"
# include "BasicTypeContainers.h"
# include "ParticleTypes.h"
# include "GenericStats.h"
# include "ItemEntity.h"
const double Mob : : MIN_MOVEMENT_DISTANCE = 0.005 ;
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 ;
}
Mob : : Mob ( Level * level ) : Entity ( level )
{
_init ( ) ;
// 4J Stu - This will not call the correct derived function, so moving to each derived class
//health = getMaxHealth();
health = 0 ;
blocksBuilding = true ;
lookControl = new LookControl ( this ) ;
moveControl = new MoveControl ( this ) ;
jumpControl = new JumpControl ( this ) ;
bodyControl = new BodyControl ( this ) ;
navigation = new PathNavigation ( this , level , 16 ) ;
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 ;
}
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 ;
if ( bodyControl ! = NULL ) delete bodyControl ;
if ( navigation ! = NULL ) delete navigation ;
if ( sensing ! = NULL ) delete sensing ;
delete restrictCenter ;
}
LookControl * Mob : : getLookControl ( )
{
return lookControl ;
}
MoveControl * Mob : : getMoveControl ( )
{
return moveControl ;
}
JumpControl * Mob : : getJumpControl ( )
{
return jumpControl ;
}
PathNavigation * Mob : : getNavigation ( )
{
return navigation ;
}
Sensing * Mob : : getSensing ( )
{
return sensing ;
}
Random * Mob : : getRandom ( )
{
return random ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Mob > Mob : : getLastHurtByMob ( )
2026-03-01 12:16:08 +08:00
{
return lastHurtByMob ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Mob > Mob : : getLastHurtMob ( )
2026-03-01 12:16:08 +08:00
{
return lastHurtMob ;
}
2026-03-02 15:58:20 +07:00
void Mob : : setLastHurtMob ( std : : shared_ptr < Entity > target )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:36:56 +07:00
std : : shared_ptr < Mob > mob = dynamic_pointer_cast < Mob > ( target ) ;
2026-03-01 12:16:08 +08:00
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 ) ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : doHurtTarget ( std : : shared_ptr < Entity > target )
2026-03-01 12:16:08 +08:00
{
setLastHurtMob ( target ) ;
return false ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Mob > Mob : : getTarget ( )
2026-03-01 12:16:08 +08:00
{
return target ;
}
2026-03-02 15:58:20 +07:00
void Mob : : setTarget ( std : : shared_ptr < Mob > target )
2026-03-01 12:16:08 +08:00
{
this - > target = target ;
}
bool Mob : : canAttackType ( eINSTANCEOF targetType )
{
return ! ( targetType = = eTYPE_CREEPER | | targetType = = eTYPE_GHAST ) ;
}
// Called by eatTileGoal
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 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : setLastHurtByMob ( std : : shared_ptr < Mob > hurtBy )
2026-03-01 12:16:08 +08:00
{
lastHurtByMob = hurtBy ;
lastHurtByMobTime = lastHurtByMob ! = NULL ? PLAYER_HURT_EXPERIENCE_TIME : 0 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : defineSynchedData ( )
2026-03-01 12:16:08 +08:00
{
entityData - > define ( DATA_EFFECT_COLOR_ID , effectColor ) ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : canSee ( std : : shared_ptr < Entity > target )
2026-03-01 12:16:08 +08:00
{
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 ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getTexture ( )
2026-03-01 12:16:08 +08:00
{
return textureIdx ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : isPickable ( )
2026-03-01 12:16:08 +08:00
{
return ! removed ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : isPushable ( )
2026-03-01 12:16:08 +08:00
{
return ! removed ;
}
2026-03-02 15:58:20 +07:00
float Mob : : getHeadHeight ( )
2026-03-01 12:16:08 +08:00
{
return bbHeight * 0.85f ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getAmbientSoundInterval ( )
2026-03-01 12:16:08 +08:00
{
return 20 * 4 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : playAmbientSound ( )
2026-03-01 12:16:08 +08:00
{
MemSect ( 31 ) ;
int ambient = getAmbientSound ( ) ;
2026-03-02 15:58:20 +07:00
if ( ambient ! = - 1 )
2026-03-01 12:16:08 +08:00
{
level - > playSound ( shared_from_this ( ) , ambient , getSoundVolume ( ) , getVoicePitch ( ) ) ;
}
MemSect ( 0 ) ;
}
2026-03-02 15:58:20 +07:00
void Mob : : baseTick ( )
2026-03-01 12:16:08 +08:00
{
oAttackAnim = attackAnim ;
Entity : : baseTick ( ) ;
2026-03-02 15:58:20 +07:00
if ( isAlive ( ) & & random - > nextInt ( 1000 ) < ambientSoundTime + + )
2026-03-01 12:16:08 +08:00
{
ambientSoundTime = - getAmbientSoundInterval ( ) ;
2026-03-02 15:58:20 +07:00
playAmbientSound ( ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 15:58:20 +07:00
if ( isAlive ( ) & & isInWall ( ) )
2026-03-01 12:16:08 +08:00
{
hurt ( DamageSource : : inWall , 1 ) ;
}
if ( isFireImmune ( ) | | level - > isClientSide ) clearFire ( ) ;
2026-03-02 15:58:20 +07:00
if ( isAlive ( ) & & isUnderLiquid ( Material : : water ) & & ! isWaterMob ( ) & & activeEffects . find ( MobEffect : : waterBreathing - > id ) = = activeEffects . end ( ) )
2026-03-01 12:16:08 +08:00
{
setAirSupply ( decreaseAirSupply ( getAirSupply ( ) ) ) ;
if ( getAirSupply ( ) = = - 20 )
{
setAirSupply ( 0 ) ;
if ( canCreateParticles ( ) )
{
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 ) ;
}
}
hurt ( DamageSource : : drown , 2 ) ;
}
clearFire ( ) ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
setAirSupply ( TOTAL_AIR_SUPPLY ) ;
}
oTilt = tilt ;
if ( attackTime > 0 ) attackTime - - ;
if ( hurtTime > 0 ) hurtTime - - ;
if ( invulnerableTime > 0 ) invulnerableTime - - ;
2026-03-02 15:58:20 +07:00
if ( health < = 0 )
2026-03-01 12:16:08 +08:00
{
tickDeath ( ) ;
}
if ( lastHurtByPlayerTime > 0 ) lastHurtByPlayerTime - - ;
else
{
2026-03-02 15:58:20 +07:00
// Note - this used to just set to nullptr, but that has to create a new std::shared_ptr and free an old one, when generally this won't be doing anything at all. This
2026-03-01 12:16:08 +08:00
// is the lightweight but ugly alternative
if ( lastHurtByPlayer )
{
lastHurtByPlayer . reset ( ) ;
}
}
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 ( )
2026-03-02 15:58:20 +07:00
{
2026-03-01 12:16:08 +08:00
deathTime + + ;
2026-03-02 15:58:20 +07:00
if ( deathTime = = 20 )
2026-03-01 12:16:08 +08:00
{
// 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 ;
2026-03-02 15:58:20 +07:00
level - > addEntity ( std : : shared_ptr < ExperienceOrb > ( new ExperienceOrb ( level , x , y , z , newCount ) ) ) ;
2026-03-01 12:16:08 +08:00
}
}
}
remove ( ) ;
2026-03-02 15:58:20 +07:00
for ( int i = 0 ; i < 20 ; i + + )
2026-03-01 12:16:08 +08:00
{
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 ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getExperienceReward ( std : : shared_ptr < Player > killedBy )
2026-03-01 12:16:08 +08:00
{
return xpReward ;
}
bool Mob : : isAlwaysExperienceDropper ( )
{
return false ;
}
2026-03-02 15:58:20 +07:00
void Mob : : spawnAnim ( )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
for ( int i = 0 ; i < 20 ; i + + )
2026-03-01 12:16:08 +08:00
{
double xa = random - > nextGaussian ( ) * 0.02 ;
double ya = random - > nextGaussian ( ) * 0.02 ;
double za = random - > nextGaussian ( ) * 0.02 ;
double dd = 10 ;
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
* dd , xa , ya , za ) ;
}
}
2026-03-02 15:58:20 +07:00
void Mob : : rideTick ( )
2026-03-01 12:16:08 +08:00
{
Entity : : rideTick ( ) ;
oRun = run ;
run = 0 ;
fallDistance = 0 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : lerpTo ( double x , double y , double z , float yRot , float xRot , int steps )
2026-03-01 12:16:08 +08:00
{
heightOffset = 0 ;
lx = x ;
ly = y ;
lz = z ;
lyr = yRot ;
lxr = xRot ;
lSteps = steps ;
}
2026-03-02 15:58:20 +07:00
void Mob : : superTick ( )
2026-03-01 12:16:08 +08:00
{
Entity : : tick ( ) ;
}
2026-03-02 15:58:20 +07:00
void Mob : : tick ( )
2026-03-01 12:16:08 +08:00
{
Entity : : 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 ;
2026-03-02 15:58:20 +07:00
if ( sideDist < = 0.05f * 0.05f )
2026-03-01 12:16:08 +08:00
{
// animStep = 0;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
tRun = 1 ;
walkSpeed = sqrt ( sideDist ) * 3 ;
yBodyRotT = ( ( float ) atan2 ( zd , xd ) * 180 / ( float ) PI - 90 ) ;
}
2026-03-02 15:58:20 +07:00
if ( attackAnim > 0 )
2026-03-01 12:16:08 +08:00
{
yBodyRotT = yRot ;
}
2026-03-02 15:58:20 +07:00
if ( ! onGround )
2026-03-01 12:16:08 +08:00
{
tRun = 0 ;
}
run = run + ( tRun - run ) * 0.3f ;
/*
* float yBodyRotD = yRot - yBodyRot ; while ( yBodyRotD < - 180 ) yBodyRotD
* + = 360 ; while ( yBodyRotD > = 180 ) yBodyRotD - = 360 ; yBodyRot + =
* yBodyRotD * 0.1f ;
*/
if ( useNewAi ( ) )
{
bodyControl - > clientTick ( ) ;
}
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 ;
2026-03-02 15:58:20 +07:00
if ( headDiff * headDiff > 50 * 50 )
2026-03-01 12:16:08 +08:00
{
yBodyRot + = headDiff * 0.2f ;
}
2026-03-02 15:58:20 +07:00
if ( behind )
2026-03-01 12:16:08 +08:00
{
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 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : heal ( int heal )
2026-03-01 12:16:08 +08:00
{
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 ( ) ;
}
}
2026-03-02 15:58:20 +07:00
bool Mob : : hurt ( DamageSource * source , int dmg )
2026-03-01 12:16:08 +08:00
{
// 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.
2026-03-02 17:36:56 +07:00
std : : shared_ptr < Player > plr = dynamic_pointer_cast < Player > ( shared_from_this ( ) ) ;
2026-03-01 12:16:08 +08:00
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 ;
2026-03-02 15:58:20 +07:00
if ( invulnerableTime > invulnerableDuration / 2.0f )
2026-03-01 12:16:08 +08:00
{
if ( dmg < = lastHurt ) return false ;
if ( ! level - > isClientSide ) actuallyHurt ( source , dmg - lastHurt ) ;
lastHurt = dmg ;
sound = false ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
lastHurt = dmg ;
lastHealth = health ;
invulnerableTime = invulnerableDuration ;
if ( ! level - > isClientSide ) actuallyHurt ( source , dmg ) ;
hurtTime = hurtDuration = 10 ;
}
hurtDir = 0 ;
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Entity > sourceEntity = source - > getEntity ( ) ;
2026-03-01 12:16:08 +08:00
if ( sourceEntity ! = NULL )
{
2026-03-02 17:36:56 +07:00
if ( dynamic_pointer_cast < Mob > ( sourceEntity ) ! = NULL ) {
setLastHurtByMob ( dynamic_pointer_cast < Mob > ( sourceEntity ) ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:36:56 +07:00
if ( dynamic_pointer_cast < Player > ( sourceEntity ) ! = NULL )
2026-03-01 12:16:08 +08:00
{
lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME ;
2026-03-02 17:36:56 +07:00
lastHurtByPlayer = dynamic_pointer_cast < Player > ( sourceEntity ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:36:56 +07:00
else if ( dynamic_pointer_cast < Wolf > ( sourceEntity ) )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:36:56 +07:00
std : : shared_ptr < Wolf > w = dynamic_pointer_cast < Wolf > ( sourceEntity ) ;
2026-03-01 12:16:08 +08:00
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 ( ) ;
2026-03-02 15:58:20 +07:00
if ( sourceEntity ! = NULL )
2026-03-01 12:16:08 +08:00
{
double xd = sourceEntity - > x - x ;
double zd = sourceEntity - > z - z ;
2026-03-02 15:58:20 +07:00
while ( xd * xd + zd * zd < 0.0001 )
2026-03-01 12:16:08 +08:00
{
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 ) ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
hurtDir = ( float ) ( int ) ( ( Math : : random ( ) * 2 ) * 180 ) ; // 4J This cast is the same as Java
}
}
MemSect ( 31 ) ;
2026-03-02 15:58:20 +07:00
if ( health < = 0 )
2026-03-01 12:16:08 +08:00
{
if ( sound ) level - > playSound ( shared_from_this ( ) , getDeathSound ( ) , getSoundVolume ( ) , getVoicePitch ( ) ) ;
die ( source ) ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
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 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : animateHurt ( )
2026-03-01 12:16:08 +08:00
{
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 damage ;
}
2026-03-02 15:58:20 +07:00
void Mob : : actuallyHurt ( DamageSource * source , int dmg )
2026-03-01 12:16:08 +08:00
{
dmg = getDamageAfterArmorAbsorb ( source , dmg ) ;
dmg = getDamageAfterMagicAbsorb ( source , dmg ) ;
health - = dmg ;
}
2026-03-02 15:58:20 +07:00
float Mob : : getSoundVolume ( )
2026-03-01 12:16:08 +08:00
{
return 1 ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getAmbientSound ( )
2026-03-01 12:16:08 +08:00
{
return - 1 ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getHurtSound ( )
2026-03-01 12:16:08 +08:00
{
return eSoundType_DAMAGE_HURT ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getDeathSound ( )
2026-03-01 12:16:08 +08:00
{
return eSoundType_DAMAGE_HURT ;
}
2026-03-02 15:58:20 +07:00
void Mob : : knockback ( std : : shared_ptr < Entity > source , int dmg , double xd , double zd )
2026-03-01 12:16:08 +08:00
{
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 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : die ( DamageSource * source )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Entity > sourceEntity = source - > getEntity ( ) ;
2026-03-01 12:16:08 +08:00
if ( deathScore > = 0 & & sourceEntity ! = NULL ) sourceEntity - > awardKillScore ( shared_from_this ( ) , deathScore ) ;
2026-03-02 17:36:56 +07:00
if ( sourceEntity ! = NULL ) sourceEntity - > killed ( dynamic_pointer_cast < Mob > ( shared_from_this ( ) ) ) ;
2026-03-01 12:16:08 +08:00
dead = true ;
2026-03-02 15:58:20 +07:00
if ( ! level - > isClientSide )
2026-03-01 12:16:08 +08:00
{
int playerBonus = 0 ;
2026-03-02 17:36:56 +07:00
std : : shared_ptr < Player > player = dynamic_pointer_cast < Player > ( sourceEntity ) ;
2026-03-01 12:16:08 +08:00
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 )
{
2026-03-02 17:36:56 +07:00
player - > awardStat ( GenericStats : : killMob ( ) , GenericStats : : param_mobKill ( player , dynamic_pointer_cast < Mob > ( shared_from_this ( ) ) , source ) ) ;
2026-03-01 12:16:08 +08:00
}
}
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 .
2026-03-02 15:58:20 +07:00
*
2026-03-01 12:16:08 +08:00
* @ param rareLootLevel
*/
void Mob : : dropRareDeathLoot ( int rareLootLevel )
{
}
2026-03-02 15:58:20 +07:00
void Mob : : dropDeathLoot ( bool wasKilledByPlayer , int playerBonusLevel )
2026-03-01 12:16:08 +08:00
{
int loot = getDeathLoot ( ) ;
2026-03-02 15:58:20 +07:00
if ( loot > 0 )
2026-03-01 12:16:08 +08:00
{
int count = random - > nextInt ( 3 ) ;
if ( playerBonusLevel > 0 )
{
count + = random - > nextInt ( playerBonusLevel + 1 ) ;
}
for ( int i = 0 ; i < count ; i + + )
spawnAtLocation ( loot , 1 ) ;
}
}
2026-03-02 15:58:20 +07:00
int Mob : : getDeathLoot ( )
2026-03-01 12:16:08 +08:00
{
return 0 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : causeFallDamage ( float distance )
2026-03-01 12:16:08 +08:00
{
Entity : : causeFallDamage ( distance ) ;
int dmg = ( int ) ceil ( distance - 3 ) ;
2026-03-02 15:58:20 +07:00
if ( dmg > 0 )
2026-03-01 12:16:08 +08:00
{
// 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 ) ) ;
2026-03-02 15:58:20 +07:00
if ( t > 0 )
2026-03-01 12:16:08 +08:00
{
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 ) ;
}
}
}
2026-03-02 15:58:20 +07:00
void Mob : : travel ( float xa , float ya )
2026-03-01 12:16:08 +08:00
{
# ifdef __PSVITA__
2026-03-02 17:36:56 +07:00
// AP - dynamic_pointer_cast is a non-trivial call
2026-03-01 12:16:08 +08:00
Player * thisPlayer = NULL ;
if ( ( GetType ( ) & eTYPE_PLAYER ) = = eTYPE_PLAYER )
{
thisPlayer = ( Player * ) this ;
}
# else
2026-03-02 17:36:56 +07:00
std : : shared_ptr < Player > thisPlayer = dynamic_pointer_cast < Player > ( shared_from_this ( ) ) ;
2026-03-01 12:16:08 +08:00
# endif
2026-03-02 15:58:20 +07:00
if ( isInWater ( ) & & ! ( thisPlayer & & thisPlayer - > abilities . flying ) )
2026-03-01 12:16:08 +08:00
{
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 ;
2026-03-02 15:58:20 +07:00
if ( horizontalCollision & & isFree ( xd , yd + 0.6f - y + yo , zd ) )
2026-03-01 12:16:08 +08:00
{
yd = 0.3f ;
}
2026-03-02 15:58:20 +07:00
}
else if ( isInLava ( ) & & ! ( thisPlayer & & thisPlayer - > abilities . flying ) )
2026-03-01 12:16:08 +08:00
{
double yo = y ;
moveRelative ( xa , ya , 0.02f ) ;
move ( xd , yd , zd ) ;
xd * = 0.50f ;
yd * = 0.50f ;
zd * = 0.50f ;
yd - = 0.02 ;
2026-03-02 15:58:20 +07:00
if ( horizontalCollision & & isFree ( xd , yd + 0.6f - y + yo , zd ) )
2026-03-01 12:16:08 +08:00
{
yd = 0.3f ;
}
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
float friction = 0.91f ;
2026-03-02 15:58:20 +07:00
if ( onGround )
2026-03-01 12:16:08 +08:00
{
friction = 0.6f * 0.91f ;
int t = level - > getTile ( Mth : : floor ( x ) , Mth : : floor ( bb - > y0 ) - 1 , Mth : : floor ( z ) ) ;
2026-03-02 15:58:20 +07:00
if ( t > 0 )
2026-03-01 12:16:08 +08:00
{
friction = Tile : : tiles [ t ] - > friction * 0.91f ;
}
}
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 ;
2026-03-02 15:58:20 +07:00
if ( onGround )
2026-03-01 12:16:08 +08:00
{
friction = 0.6f * 0.91f ;
int t = level - > getTile ( Mth : : floor ( x ) , Mth : : floor ( bb - > y0 ) - 1 , Mth : : floor ( z ) ) ;
2026-03-02 15:58:20 +07:00
if ( t > 0 )
2026-03-01 12:16:08 +08:00
{
friction = Tile : : tiles [ t ] - > friction * 0.91f ;
}
}
2026-03-02 15:58:20 +07:00
if ( onLadder ( ) )
2026-03-01 12:16:08 +08:00
{
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 ;
2026-03-02 17:36:56 +07:00
bool playerSneaking = isSneaking ( ) & & dynamic_pointer_cast < Player > ( shared_from_this ( ) ) ! = NULL ;
2026-03-01 12:16:08 +08:00
if ( playerSneaking & & yd < 0 ) yd = 0 ;
}
move ( xd , yd , zd ) ;
2026-03-02 15:58:20 +07:00
if ( horizontalCollision & & onLadder ( ) )
2026-03-01 12:16:08 +08:00
{
yd = 0.2 ;
}
yd - = 0.08 ;
yd * = 0.98f ;
xd * = friction ;
zd * = friction ;
}
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 ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : onLadder ( )
2026-03-01 12:16:08 +08:00
{
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 ) ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : isShootable ( )
2026-03-01 12:16:08 +08:00
{
return true ;
}
2026-03-02 15:58:20 +07:00
void Mob : : addAdditonalSaveData ( CompoundTag * entityTag )
2026-03-01 12:16:08 +08:00
{
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 ( ) )
{
ListTag < CompoundTag > * listTag = new ListTag < CompoundTag > ( ) ;
for ( AUTO_VAR ( it , activeEffects . begin ( ) ) ; it ! = activeEffects . end ( ) ; + + it )
{
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 ) ;
}
entityTag - > put ( L " ActiveEffects " , listTag ) ;
}
}
2026-03-02 15:58:20 +07:00
void Mob : : readAdditionalSaveData ( CompoundTag * tag )
2026-03-01 12:16:08 +08:00
{
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 " ) )
{
ListTag < CompoundTag > * effects = ( ListTag < CompoundTag > * ) tag - > getList ( L " ActiveEffects " ) ;
for ( int i = 0 ; i < effects - > 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 ) ) ) ;
}
}
}
2026-03-02 15:58:20 +07:00
bool Mob : : isAlive ( )
2026-03-01 12:16:08 +08:00
{
return ! removed & & health > 0 ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : isWaterMob ( )
2026-03-01 12:16:08 +08:00
{
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 ] ) ;
}
void Mob : : setYya ( float yya )
{
this - > yya = yya ;
}
void Mob : : setJumping ( bool jump )
{
jumping = jump ;
}
2026-03-02 15:58:20 +07:00
void Mob : : aiStep ( )
2026-03-01 12:16:08 +08:00
{
if ( noJumpDelay > 0 ) noJumpDelay - - ;
2026-03-02 15:58:20 +07:00
if ( lSteps > 0 )
2026-03-01 12:16:08 +08:00
{
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 ;
}
2026-03-02 15:58:20 +07:00
if ( isImmobile ( ) )
2026-03-01 12:16:08 +08:00
{
jumping = false ;
xxa = 0 ;
yya = 0 ;
yRotA = 0 ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
MemSect ( 25 ) ;
if ( isEffectiveAI ( ) )
{
if ( useNewAi ( ) )
{
newServerAiStep ( ) ;
}
else
{
serverAiStep ( ) ;
yHeadRot = yRot ;
}
}
MemSect ( 0 ) ;
}
2026-03-02 15:58:20 +07:00
if ( jumping )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
if ( isInWater ( ) | | isInLava ( ) )
2026-03-01 12:16:08 +08:00
{
yd + = 0.04f ;
}
2026-03-02 15:58:20 +07:00
else if ( onGround )
2026-03-01 12:16:08 +08:00
{
if ( noJumpDelay = = 0 )
{
jumpFromGround ( ) ;
noJumpDelay = 10 ;
}
}
}
else
{
noJumpDelay = 0 ;
}
xxa * = 0.98f ;
yya * = 0.98f ;
yRotA * = 0.9f ;
float normalSpeed = walkingSpeed ;
walkingSpeed * = getWalkingSpeedModifier ( ) ;
travel ( xxa , yya ) ;
walkingSpeed = normalSpeed ;
if ( ! level - > isClientSide )
{
2026-03-02 15:58:20 +07:00
vector < std : : shared_ptr < Entity > > * entities = level - > getEntities ( shared_from_this ( ) , this - > bb - > grow ( 0.2f , 0 , 0.2f ) ) ;
if ( entities ! = NULL & & ! entities - > empty ( ) )
2026-03-01 12:16:08 +08:00
{
AUTO_VAR ( itEnd , entities - > end ( ) ) ;
for ( AUTO_VAR ( it , entities - > begin ( ) ) ; it ! = itEnd ; it + + )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Entity > e = * it ; //entities->at(i);
2026-03-01 12:16:08 +08:00
if ( e - > isPushable ( ) ) e - > push ( shared_from_this ( ) ) ;
}
}
}
}
bool Mob : : useNewAi ( )
{
return false ;
}
bool Mob : : isEffectiveAI ( )
{
return ! level - > isClientSide ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : isImmobile ( )
2026-03-01 12:16:08 +08:00
{
return health < = 0 ;
}
bool Mob : : isBlocking ( )
{
return false ;
}
2026-03-02 15:58:20 +07:00
void Mob : : jumpFromGround ( )
2026-03-01 12:16:08 +08:00
{
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 ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : removeWhenFarAway ( )
2026-03-01 12:16:08 +08:00
{
return true ;
}
2026-03-02 15:58:20 +07:00
void Mob : : checkDespawn ( )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Entity > player = level - > getNearestPlayer ( shared_from_this ( ) , - 1 ) ;
if ( player ! = NULL )
2026-03-01 12:16:08 +08:00
{
double xd = player - > x - x ;
double yd = player - > y - y ;
double zd = player - > z - z ;
double sd = xd * xd + yd * yd + zd * zd ;
2026-03-02 15:58:20 +07:00
if ( removeWhenFarAway ( ) & & sd > 128 * 128 )
2026-03-01 12:16:08 +08:00
{
remove ( ) ;
}
2026-03-02 15:58:20 +07:00
if ( noActionTime > 20 * 30 & & random - > nextInt ( 800 ) = = 0 & & sd > 32 * 32 & & removeWhenFarAway ( ) )
2026-03-01 12:16:08 +08:00
{
remove ( ) ;
}
2026-03-02 15:58:20 +07:00
else if ( sd < 32 * 32 )
2026-03-01 12:16:08 +08:00
{
noActionTime = 0 ;
}
}
}
void Mob : : newServerAiStep ( )
{
MemSect ( 51 ) ;
noActionTime + + ;
checkDespawn ( ) ;
sensing - > tick ( ) ;
targetSelector . tick ( ) ;
goalSelector . tick ( ) ;
navigation - > tick ( ) ;
serverAiMobStep ( ) ;
moveControl - > tick ( ) ;
lookControl - > tick ( ) ;
jumpControl - > tick ( ) ;
// 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.
considerForExtraWandering ( isDespawnProtected ( ) ) ;
MemSect ( 0 ) ;
}
void Mob : : serverAiMobStep ( )
{
}
2026-03-02 15:58:20 +07:00
void Mob : : serverAiStep ( )
2026-03-01 12:16:08 +08:00
{
noActionTime + + ;
checkDespawn ( ) ;
xxa = 0 ;
yya = 0 ;
float lookDistance = 8 ;
2026-03-02 15:58:20 +07:00
if ( random - > nextFloat ( ) < 0.02f )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Player > player = level - > getNearestPlayer ( shared_from_this ( ) , lookDistance ) ;
if ( player ! = NULL )
2026-03-01 12:16:08 +08:00
{
lookingAt = player ;
lookTime = 10 + random - > nextInt ( 20 ) ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
yRotA = ( random - > nextFloat ( ) - 0.5f ) * 20 ;
}
}
if ( lookingAt ! = NULL )
{
lookAt ( lookingAt , 10.0f , ( float ) getMaxHeadXRot ( ) ) ;
2026-03-02 15:58:20 +07:00
if ( lookTime - - < = 0 | | lookingAt - > removed | | lookingAt - > distanceToSqr ( shared_from_this ( ) ) > lookDistance * lookDistance )
2026-03-01 12:16:08 +08:00
{
lookingAt = nullptr ;
}
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
if ( random - > nextFloat ( ) < 0.05f )
2026-03-01 12:16:08 +08:00
{
yRotA = ( random - > nextFloat ( ) - 0.5f ) * 20 ;
}
yRot + = yRotA ;
xRot = defaultLookAngle ;
}
bool inWater = isInWater ( ) ;
bool inLava = isInLava ( ) ;
if ( inWater | | inLava ) jumping = random - > nextFloat ( ) < 0.8f ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getMaxHeadXRot ( )
2026-03-01 12:16:08 +08:00
{
return 40 ;
}
2026-03-02 15:58:20 +07:00
void Mob : : lookAt ( std : : shared_ptr < Entity > e , float yMax , float xMax )
2026-03-01 12:16:08 +08:00
{
double xd = e - > x - x ;
double yd ;
double zd = e - > z - z ;
2026-03-02 15:58:20 +07:00
2026-03-02 17:36:56 +07:00
std : : shared_ptr < Mob > mob = dynamic_pointer_cast < Mob > ( e ) ;
2026-03-01 12:16:08 +08:00
if ( mob ! = NULL )
{
yd = ( y + getHeadHeight ( ) ) - ( mob - > y + mob - > getHeadHeight ( ) ) ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
yd = ( e - > bb - > y0 + e - > bb - > y1 ) / 2 - ( y + getHeadHeight ( ) ) ;
}
double sd = Mth : : sqrt ( xd * xd + zd * zd ) ;
float yRotD = ( float ) ( atan2 ( zd , xd ) * 180 / PI ) - 90 ;
float xRotD = ( float ) - ( atan2 ( yd , sd ) * 180 / PI ) ;
xRot = - rotlerp ( xRot , xRotD , xMax ) ;
yRot = rotlerp ( yRot , yRotD , yMax ) ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : isLookingAtAnEntity ( )
2026-03-01 12:16:08 +08:00
{
return lookingAt ! = NULL ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Entity > Mob : : getLookingAt ( )
2026-03-01 12:16:08 +08:00
{
return lookingAt ;
}
2026-03-02 15:58:20 +07:00
float Mob : : rotlerp ( float a , float b , float max )
2026-03-01 12:16:08 +08:00
{
float diff = Mth : : wrapDegrees ( b - a ) ;
2026-03-02 15:58:20 +07:00
if ( diff > max )
2026-03-01 12:16:08 +08:00
{
diff = max ;
}
2026-03-02 15:58:20 +07:00
if ( diff < - max )
2026-03-01 12:16:08 +08:00
{
diff = - max ;
}
return a + diff ;
}
2026-03-02 15:58:20 +07:00
bool Mob : : canSpawn ( )
2026-03-01 12:16:08 +08:00
{
// 4J - altered to use special containsAnyLiquid variant
return level - > isUnobstructed ( bb ) & & level - > getCubes ( shared_from_this ( ) , bb ) - > empty ( ) & & ! level - > containsAnyLiquid_NoLoad ( bb ) ;
}
2026-03-02 15:58:20 +07:00
void Mob : : outOfWorld ( )
2026-03-01 12:16:08 +08:00
{
hurt ( DamageSource : : outOfWorld , 4 ) ;
}
2026-03-02 15:58:20 +07:00
float Mob : : getAttackAnim ( float a )
2026-03-01 12:16:08 +08:00
{
float diff = attackAnim - oAttackAnim ;
if ( diff < 0 ) diff + = 1 ;
return oAttackAnim + diff * a ;
}
2026-03-02 15:58:20 +07:00
Vec3 * Mob : : getPos ( float a )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
if ( a = = 1 )
2026-03-01 12:16:08 +08:00
{
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 ) ;
}
2026-03-02 15:58:20 +07:00
Vec3 * Mob : : getLookAngle ( )
2026-03-01 12:16:08 +08:00
{
return getViewVector ( 1 ) ;
}
2026-03-02 15:58:20 +07:00
Vec3 * Mob : : getViewVector ( float a )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
if ( a = = 1 )
2026-03-01 12:16:08 +08:00
{
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 ;
}
float Mob : : getHeadSizeScale ( )
{
return 1.0f ;
}
2026-03-02 15:58:20 +07:00
HitResult * Mob : : pick ( double range , float a )
2026-03-01 12:16:08 +08:00
{
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 ) ;
}
2026-03-02 15:58:20 +07:00
int Mob : : getMaxSpawnClusterSize ( )
2026-03-01 12:16:08 +08:00
{
return 4 ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemInstance > Mob : : getCarriedItem ( )
2026-03-01 12:16:08 +08:00
{
return nullptr ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemInstance > Mob : : getArmor ( int pos )
2026-03-01 12:16:08 +08:00
{
// 4J Stu - Not implemented yet
return nullptr ;
//return equipment[pos + 1];
}
2026-03-02 15:58:20 +07:00
void Mob : : handleEntityEvent ( byte id )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
if ( id = = EntityEvent : : HURT )
2026-03-01 12:16:08 +08:00
{
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 )
2026-03-02 15:58:20 +07:00
{
2026-03-01 12:16:08 +08:00
level - > playSound ( shared_from_this ( ) , iHurtSound , getSoundVolume ( ) , ( random - > nextFloat ( ) - random - > nextFloat ( ) ) * 0.2f + 1.0f ) ;
}
MemSect ( 0 ) ;
hurt ( DamageSource : : genericSource , 0 ) ;
2026-03-02 15:58:20 +07:00
}
else if ( id = = EntityEvent : : DEATH )
2026-03-01 12:16:08 +08:00
{
MemSect ( 31 ) ;
// 4J-PB -added because villagers have no sounds
int iDeathSound = getDeathSound ( ) ;
if ( iDeathSound ! = - 1 )
2026-03-02 15:58:20 +07:00
{
2026-03-01 12:16:08 +08:00
level - > playSound ( shared_from_this ( ) , iDeathSound , getSoundVolume ( ) , ( random - > nextFloat ( ) - random - > nextFloat ( ) ) * 0.2f + 1.0f ) ;
}
MemSect ( 0 ) ;
health = 0 ;
die ( DamageSource : : genericSource ) ;
2026-03-02 15:58:20 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
Entity : : handleEntityEvent ( id ) ;
}
}
2026-03-02 15:58:20 +07:00
bool Mob : : isSleeping ( )
2026-03-01 12:16:08 +08:00
{
return false ;
}
2026-03-02 15:58:20 +07:00
Icon * Mob : : getItemInHandIcon ( std : : shared_ptr < ItemInstance > item , int layer )
2026-03-01 12:16:08 +08:00
{
return item - > getIcon ( ) ;
}
// 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 : : tickEffects ( )
{
bool removed = false ;
for ( AUTO_VAR ( it , activeEffects . begin ( ) ) ; it ! = activeEffects . end ( ) ; )
{
MobEffectInstance * effect = it - > second ;
removed = false ;
2026-03-02 17:36:56 +07:00
if ( ! effect - > tick ( dynamic_pointer_cast < Mob > ( shared_from_this ( ) ) ) )
2026-03-01 12:16:08 +08:00
{
if ( ! level - > isClientSide )
{
it = activeEffects . erase ( it ) ;
onEffectRemoved ( effect ) ;
delete effect ;
removed = true ;
}
}
if ( ! removed )
{
+ + it ;
}
}
if ( effectsDirty )
{
if ( ! level - > isClientSide )
{
if ( activeEffects . empty ( ) )
{
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 ) ) ;
}
}
effectsDirty = false ;
}
if ( random - > nextBoolean ( ) )
{
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 ;
level - > addParticle ( eParticleType_mobSpell , x + ( random - > nextDouble ( ) - 0.5 ) * bbWidth , y + random - > nextDouble ( ) * bbHeight - heightOffset , z + ( random - > nextDouble ( ) - 0.5 ) * bbWidth , red , green , blue ) ;
}
}
}
void Mob : : removeAllEffects ( )
{
//Iterator<Integer> effectIdIterator = activeEffects.keySet().iterator();
//while (effectIdIterator.hasNext())
for ( AUTO_VAR ( it , activeEffects . begin ( ) ) ; it ! = activeEffects . end ( ) ; )
{
//Integer effectId = effectIdIterator.next();
MobEffectInstance * effect = it - > second ; //activeEffects.get(effectId);
if ( ! level - > isClientSide )
{
//effectIdIterator.remove();
it = activeEffects . erase ( it ) ;
onEffectRemoved ( effect ) ;
delete effect ;
}
else
{
+ + it ;
}
}
}
vector < MobEffectInstance * > * Mob : : getActiveEffects ( )
{
vector < MobEffectInstance * > * active = new vector < MobEffectInstance * > ( ) ;
for ( AUTO_VAR ( it , activeEffects . begin ( ) ) ; it ! = activeEffects . end ( ) ; + + it )
{
active - > push_back ( it - > second ) ;
}
return active ;
}
bool Mob : : hasEffect ( int id )
{
return activeEffects . find ( id ) ! = activeEffects . end ( ) ; ;
}
bool Mob : : hasEffect ( MobEffect * effect )
{
return activeEffects . find ( effect - > id ) ! = activeEffects . end ( ) ;
}
MobEffectInstance * Mob : : getEffect ( MobEffect * effect )
{
MobEffectInstance * effectInst = NULL ;
AUTO_VAR ( it , activeEffects . find ( effect - > id ) ) ;
if ( it ! = activeEffects . end ( ) ) effectInst = it - > second ;
return effectInst ;
}
void Mob : : addEffect ( MobEffectInstance * newEffect )
{
if ( ! canBeAffected ( newEffect ) )
{
return ;
}
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 ) ;
}
}
// 4J Added
void Mob : : addEffectNoUpdate ( MobEffectInstance * newEffect )
{
if ( ! canBeAffected ( newEffect ) )
{
return ;
}
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 : : canBeAffected ( MobEffectInstance * newEffect )
{
if ( getMobType ( ) = = UNDEAD )
{
int id = newEffect - > getId ( ) ;
if ( id = = MobEffect : : regeneration - > id | | id = = MobEffect : : poison - > id )
{
return false ;
}
}
return true ;
}
bool Mob : : isInvertedHealAndHarm ( )
{
return getMobType ( ) = = UNDEAD ;
}
void Mob : : removeEffectNoUpdate ( int effectId )
{
AUTO_VAR ( it , activeEffects . find ( effectId ) ) ;
if ( it ! = activeEffects . end ( ) )
{
MobEffectInstance * effect = it - > second ;
if ( effect ! = NULL )
{
delete effect ;
}
activeEffects . erase ( it ) ;
}
}
void Mob : : removeEffect ( int effectId )
{
AUTO_VAR ( it , activeEffects . find ( effectId ) ) ;
if ( it ! = activeEffects . end ( ) )
{
MobEffectInstance * effect = it - > second ;
if ( effect ! = NULL )
{
onEffectRemoved ( effect ) ;
delete effect ;
}
activeEffects . erase ( it ) ;
}
}
void Mob : : onEffectAdded ( MobEffectInstance * effect )
{
effectsDirty = true ;
}
void Mob : : onEffectUpdated ( MobEffectInstance * effect )
{
effectsDirty = true ;
}
void Mob : : onEffectRemoved ( MobEffectInstance * effect )
{
effectsDirty = true ;
}
float Mob : : getWalkingSpeedModifier ( )
{
float speed = 1.0f ;
if ( hasEffect ( MobEffect : : movementSpeed ) )
{
speed * = 1.0f + .2f * ( getEffect ( MobEffect : : movementSpeed ) - > getAmplifier ( ) + 1 ) ;
}
if ( hasEffect ( MobEffect : : movementSlowdown ) )
{
speed * = 1.0f - .15f * ( getEffect ( MobEffect : : movementSlowdown ) - > getAmplifier ( ) + 1 ) ;
}
return speed ;
}
2026-03-02 15:58:20 +07:00
void Mob : : teleportTo ( double x , double y , double z )
2026-03-01 12:16:08 +08:00
{
moveTo ( x , y , z , yRot , xRot ) ;
}
bool Mob : : isBaby ( )
{
return false ;
}
MobType Mob : : getMobType ( )
{
return UNDEFINED ;
}
2026-03-02 15:58:20 +07:00
void Mob : : breakItem ( std : : shared_ptr < ItemInstance > itemInstance )
2026-03-01 12:16:08 +08:00
{
level - > playSound ( shared_from_this ( ) , eSoundType_RANDOM_BREAK , 0.8f , 0.8f + level - > random - > nextFloat ( ) * 0.4f ) ;
for ( int i = 0 ; i < 5 ; i + + )
{
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 ) ;
}
}
bool Mob : : isInvulnerable ( )
{
// 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;
}
void Mob : : setLevel ( Level * level )
{
Entity : : setLevel ( level ) ;
navigation - > setLevel ( level ) ;
goalSelector . setLevel ( level ) ;
targetSelector . setLevel ( level ) ;
}
void Mob : : finalizeMobSpawn ( )
{
}
bool Mob : : canBeControlledByRider ( )
{
return false ;
}