2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "JavaMath.h"
2026-03-03 03:04:10 +08:00
# include "net.minecraft.network.packet.h"
2026-03-01 12:16:08 +08:00
# include "net.minecraft.world.level.tile.h"
# include "net.minecraft.world.phys.h"
# include "net.minecraft.world.entity.h"
2026-03-03 03:04:10 +08:00
# include "net.minecraft.world.entity.ai.attributes.h"
2026-03-01 12:16:08 +08:00
# 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"
2026-03-03 03:04:10 +08:00
# include "net.minecraft.world.h"
# include "..\Minecraft.Client\ServerLevel.h"
# include "..\Minecraft.Client\EntityTracker.h"
2026-03-01 12:16:08 +08:00
# 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"
2026-03-03 03:04:10 +08:00
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 ;
2026-03-01 12:16:08 +08:00
void Mob : : _init ( )
{
ambientSoundTime = 0 ;
xpReward = 0 ;
defaultLookAngle = 0.0f ;
lookingAt = nullptr ;
lookTime = 0 ;
target = nullptr ;
sensing = NULL ;
2026-03-03 03:04:10 +08:00
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 ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
Mob : : Mob ( Level * level ) : LivingEntity ( level )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
MemSect ( 57 ) ;
2026-03-01 12:16:08 +08:00
_init ( ) ;
2026-03-03 03:04:10 +08:00
MemSect ( 0 ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
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 ) ;
2026-03-01 12:16:08 +08:00
lookControl = new LookControl ( this ) ;
moveControl = new MoveControl ( this ) ;
jumpControl = new JumpControl ( this ) ;
bodyControl = new BodyControl ( this ) ;
2026-03-03 03:04:10 +08:00
navigation = new PathNavigation ( this , level ) ;
2026-03-01 12:16:08 +08:00
sensing = new Sensing ( this ) ;
2026-03-03 03:04:10 +08:00
for ( int i = 0 ; i < 5 ; i + + )
{
dropChances [ i ] = 0.085f ;
}
2026-03-01 12:16:08 +08:00
}
Mob : : ~ Mob ( )
{
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 ;
2026-03-03 03:04:10 +08:00
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 ) ;
2026-03-01 12:16:08 +08:00
}
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 ;
}
2026-03-03 03:04:10 +08:00
shared_ptr < LivingEntity > Mob : : getTarget ( )
2026-03-01 12:16:08 +08:00
{
return target ;
}
2026-03-03 03:04:10 +08:00
void Mob : : setTarget ( shared_ptr < LivingEntity > 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 ( )
{
}
2026-03-02 17:37:16 +07:00
void Mob : : defineSynchedData ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
LivingEntity : : defineSynchedData ( ) ;
entityData - > define ( DATA_CUSTOM_NAME_VISIBLE , ( byte ) 0 ) ;
entityData - > define ( DATA_CUSTOM_NAME , L " " ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:37:16 +07:00
int Mob : : getAmbientSoundInterval ( )
2026-03-01 12:16:08 +08:00
{
return 20 * 4 ;
}
2026-03-02 17:37:16 +07:00
void Mob : : playAmbientSound ( )
2026-03-01 12:16:08 +08:00
{
MemSect ( 31 ) ;
int ambient = getAmbientSound ( ) ;
2026-03-02 17:37:16 +07:00
if ( ambient ! = - 1 )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
playSound ( ambient , getSoundVolume ( ) , getVoicePitch ( ) ) ;
2026-03-01 12:16:08 +08:00
}
MemSect ( 0 ) ;
}
2026-03-02 17:37:16 +07:00
void Mob : : baseTick ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
LivingEntity : : baseTick ( ) ;
2026-03-01 12:16:08 +08:00
2026-03-02 17:37:16 +07:00
if ( isAlive ( ) & & random - > nextInt ( 1000 ) < ambientSoundTime + + )
2026-03-01 12:16:08 +08:00
{
ambientSoundTime = - getAmbientSoundInterval ( ) ;
2026-03-02 17:37:16 +07:00
playAmbientSound ( ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
int Mob : : getExperienceReward ( shared_ptr < Player > killedBy )
{
if ( xpReward > 0 )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
int result = xpReward ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
ItemInstanceArray slots = getEquipmentSlots ( ) ;
for ( int i = 0 ; i < slots . length ; i + + )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( slots [ i ] ! = NULL & & dropChances [ i ] < = 1 )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
result + = 1 + random - > nextInt ( 3 ) ;
2026-03-01 12:16:08 +08:00
}
}
2026-03-03 03:04:10 +08:00
return result ;
2026-03-01 12:16:08 +08:00
}
else
{
2026-03-03 03:04:10 +08:00
return xpReward ;
2026-03-01 12:16:08 +08:00
}
}
2026-03-02 17:37:16 +07:00
void Mob : : spawnAnim ( )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:37:16 +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 17:37:16 +07:00
void Mob : : tick ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
LivingEntity : : tick ( ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
if ( ! level - > isClientSide )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
tickLeash ( ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
float Mob : : tickHeadTurn ( float yBodyRotT , float walkSpeed )
{
2026-03-01 12:16:08 +08:00
if ( useNewAi ( ) )
{
bodyControl - > clientTick ( ) ;
2026-03-03 03:04:10 +08:00
return walkSpeed ;
2026-03-01 12:16:08 +08:00
}
else
{
2026-03-03 03:04:10 +08:00
return LivingEntity : : tickHeadTurn ( yBodyRotT , walkSpeed ) ;
2026-03-01 12:16:08 +08:00
}
}
2026-03-02 17:37:16 +07:00
int Mob : : getAmbientSound ( )
2026-03-01 12:16:08 +08:00
{
return - 1 ;
}
2026-03-03 03:04:10 +08:00
int Mob : : getDeathLoot ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return 0 ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:37:16 +07:00
void Mob : : dropDeathLoot ( bool wasKilledByPlayer , int playerBonusLevel )
2026-03-01 12:16:08 +08:00
{
int loot = getDeathLoot ( ) ;
2026-03-02 17:37:16 +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 17:37:16 +07:00
void Mob : : addAdditonalSaveData ( CompoundTag * entityTag )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
LivingEntity : : addAdditonalSaveData ( entityTag ) ;
entityTag - > putBoolean ( L " CanPickUpLoot " , canPickUpLoot ( ) ) ;
entityTag - > putBoolean ( L " PersistenceRequired " , persistenceRequired ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
ListTag < CompoundTag > * gear = new ListTag < CompoundTag > ( ) ;
for ( int i = 0 ; i < equipment . length ; i + + )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
CompoundTag * tag = new CompoundTag ( ) ;
if ( equipment [ i ] ! = NULL ) equipment [ i ] - > save ( tag ) ;
gear - > add ( tag ) ;
}
entityTag - > put ( L " Equipment " , gear ) ;
ListTag < FloatTag > * dropChanceList = new ListTag < FloatTag > ( ) ;
for ( int i = 0 ; i < dropChances . length ; i + + )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
dropChanceList - > add ( new FloatTag ( _toString ( i ) , dropChances [ i ] ) ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
entityTag - > put ( L " DropChances " , dropChanceList ) ;
entityTag - > putString ( L " CustomName " , getCustomName ( ) ) ;
entityTag - > putBoolean ( L " CustomNameVisible " , isCustomNameVisible ( ) ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
// leash info
entityTag - > putBoolean ( L " Leashed " , _isLeashed ) ;
if ( leashHolder ! = NULL )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
CompoundTag * leashTag = new CompoundTag ( L " Leash " ) ;
if ( leashHolder - > instanceof ( eTYPE_LIVINGENTITY ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
// a walking, talking, leash holder
leashTag - > putString ( L " UUID " , leashHolder - > getUUID ( ) ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
else if ( leashHolder - > instanceof ( eTYPE_HANGING_ENTITY ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
// 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 ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
entityTag - > put ( L " Leash " , leashTag ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
}
void Mob : : readAdditionalSaveData ( CompoundTag * tag )
{
LivingEntity : : readAdditionalSaveData ( tag ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
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 " ) ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
if ( tag - > contains ( L " Equipment " ) )
{
ListTag < CompoundTag > * gear = ( ListTag < CompoundTag > * ) tag - > getList ( L " Equipment " ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
for ( int i = 0 ; i < equipment . length ; i + + )
{
equipment [ i ] = ItemInstance : : fromTag ( gear - > get ( i ) ) ;
}
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
if ( tag - > contains ( L " DropChances " ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
ListTag < FloatTag > * items = ( ListTag < FloatTag > * ) tag - > getList ( L " DropChances " ) ;
for ( int i = 0 ; i < items - > size ( ) ; i + + )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
dropChances [ i ] = items - > get ( i ) - > data ;
2026-03-01 12:16:08 +08:00
}
}
2026-03-03 03:04:10 +08:00
_isLeashed = tag - > getBoolean ( L " Leashed " ) ;
if ( _isLeashed & & tag - > contains ( L " Leash " ) )
{
leashInfoTag = ( CompoundTag * ) tag - > getCompound ( L " Leash " ) - > copy ( ) ;
}
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : setYya ( float yya )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
this - > yya = yya ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : setSpeed ( float speed )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
LivingEntity : : setSpeed ( speed ) ;
setYya ( speed ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : aiStep ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
LivingEntity : : aiStep ( ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
if ( ! level - > isClientSide & & canPickUpLoot ( ) & & ! dead & & level - > getGameRules ( ) - > getBoolean ( GameRules : : RULE_MOBGRIEFING ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
vector < shared_ptr < Entity > > * entities = level - > getEntitiesOfClass ( typeid ( ItemEntity ) , bb - > grow ( 1 , 0 , 1 ) ) ;
for ( AUTO_VAR ( it , entities - > begin ( ) ) ; it ! = entities - > end ( ) ; + + it )
{
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 ) ;
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
if ( slot > - 1 )
{
bool replace = true ;
shared_ptr < ItemInstance > current = getCarried ( slot ) ;
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 ( 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 ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
}
bool Mob : : useNewAi ( )
{
return false ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:37:16 +07:00
bool Mob : : removeWhenFarAway ( )
2026-03-01 12:16:08 +08:00
{
return true ;
}
2026-03-02 17:37:16 +07:00
void Mob : : checkDespawn ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( persistenceRequired )
{
noActionTime = 0 ;
return ;
}
2026-03-02 17:37:16 +07:00
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 17:37:16 +07:00
if ( removeWhenFarAway ( ) & & sd > 128 * 128 )
2026-03-01 12:16:08 +08:00
{
remove ( ) ;
}
2026-03-02 17:37:16 +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 17:37:16 +07:00
else if ( sd < 32 * 32 )
2026-03-01 12:16:08 +08:00
{
noActionTime = 0 ;
}
}
}
void Mob : : newServerAiStep ( )
{
2026-03-03 03:04:10 +08:00
PIXBeginNamedEvent ( 0 , " Tick target selector for %d " , GetType ( ) ) ;
2026-03-01 12:16:08 +08:00
MemSect ( 51 ) ;
noActionTime + + ;
2026-03-03 03:04:10 +08:00
PIXBeginNamedEvent ( 0 , " Check despawn " ) ;
2026-03-01 12:16:08 +08:00
checkDespawn ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick sensing " ) ;
sensing - > tick ( ) ;
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick target selector " ) ;
2026-03-01 12:16:08 +08:00
targetSelector . tick ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick goal selectors " ) ;
2026-03-01 12:16:08 +08:00
goalSelector . tick ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick navigation " ) ;
2026-03-01 12:16:08 +08:00
navigation - > tick ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick server ai mob step " ) ;
2026-03-01 12:16:08 +08:00
serverAiMobStep ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick move " ) ;
2026-03-01 12:16:08 +08:00
moveControl - > tick ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick look " ) ;
2026-03-01 12:16:08 +08:00
lookControl - > tick ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Tick jump " ) ;
2026-03-01 12:16:08 +08:00
jumpControl - > tick ( ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
2026-03-01 12:16:08 +08:00
// 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.
2026-03-03 03:04:10 +08:00
PIXBeginNamedEvent ( 0 , " Consider extra wandering " ) ;
2026-03-01 12:16:08 +08:00
considerForExtraWandering ( isDespawnProtected ( ) ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
2026-03-01 12:16:08 +08:00
MemSect ( 0 ) ;
2026-03-03 03:04:10 +08:00
PIXEndNamedEvent ( ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 17:37:16 +07:00
void Mob : : serverAiStep ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
LivingEntity : : serverAiStep ( ) ;
2026-03-01 12:16:08 +08:00
xxa = 0 ;
yya = 0 ;
2026-03-03 03:04:10 +08:00
checkDespawn ( ) ;
2026-03-01 12:16:08 +08:00
float lookDistance = 8 ;
2026-03-02 17:37:16 +07:00
if ( random - > nextFloat ( ) < 0.02f )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:37:16 +07:00
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 17:37:16 +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 17:37:16 +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 17:37:16 +07:00
}
else
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:37:16 +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 17:37:16 +07:00
int Mob : : getMaxHeadXRot ( )
2026-03-01 12:16:08 +08:00
{
return 40 ;
}
2026-03-02 17:37:16 +07:00
void Mob : : lookAt ( 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-03 03:04:10 +08:00
2026-03-02 17:37:16 +07:00
2026-03-03 03:04:10 +08:00
if ( e - > instanceof ( eTYPE_LIVINGENTITY ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
shared_ptr < LivingEntity > mob = dynamic_pointer_cast < LivingEntity > ( e ) ;
yd = ( mob - > y + mob - > getHeadHeight ( ) ) - ( y + getHeadHeight ( ) ) ;
2026-03-02 17:37:16 +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 ) ;
2026-03-03 03:04:10 +08:00
xRot = rotlerp ( xRot , xRotD , xMax ) ;
2026-03-01 12:16:08 +08:00
yRot = rotlerp ( yRot , yRotD , yMax ) ;
}
2026-03-02 17:37:16 +07:00
bool Mob : : isLookingAtAnEntity ( )
2026-03-01 12:16:08 +08:00
{
return lookingAt ! = NULL ;
}
2026-03-02 17:37:16 +07:00
shared_ptr < Entity > Mob : : getLookingAt ( )
2026-03-01 12:16:08 +08:00
{
return lookingAt ;
}
2026-03-02 17:37:16 +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 17:37:16 +07:00
if ( diff > max )
2026-03-01 12:16:08 +08:00
{
diff = max ;
}
2026-03-02 17:37:16 +07:00
if ( diff < - max )
2026-03-01 12:16:08 +08:00
{
diff = - max ;
}
return a + diff ;
}
2026-03-02 17:37:16 +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 ) ;
}
float Mob : : getSizeScale ( )
{
return 1.0f ;
}
float Mob : : getHeadSizeScale ( )
{
return 1.0f ;
}
2026-03-02 17:37:16 +07:00
int Mob : : getMaxSpawnClusterSize ( )
2026-03-01 12:16:08 +08:00
{
return 4 ;
}
2026-03-03 03:04:10 +08:00
int Mob : : getMaxFallDistance ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
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 ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
shared_ptr < ItemInstance > Mob : : getCarriedItem ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return equipment [ SLOT_WEAPON ] ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
shared_ptr < ItemInstance > Mob : : getCarried ( int slot )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return equipment [ slot ] ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
shared_ptr < ItemInstance > Mob : : getArmor ( int pos )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return equipment [ pos + 1 ] ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : setEquippedSlot ( int slot , shared_ptr < ItemInstance > item )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
equipment [ slot ] = item ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
ItemInstanceArray Mob : : getEquipmentSlots ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return equipment ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : dropEquipment ( bool byPlayer , int playerBonusLevel )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
for ( int slot = 0 ; slot < getEquipmentSlots ( ) . length ; slot + + )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
shared_ptr < ItemInstance > item = getCarried ( slot ) ;
bool preserve = dropChances [ slot ] > 1 ;
if ( item ! = NULL & & ( byPlayer | | preserve ) & & random - > nextFloat ( ) - playerBonusLevel * 0.01f < dropChances [ slot ] )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( ! preserve & & item - > isDamageableItem ( ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
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 ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
spawnAtLocation ( item , 0 ) ;
2026-03-01 12:16:08 +08:00
}
}
2026-03-03 03:04:10 +08:00
}
void Mob : : populateDefaultEquipmentSlots ( )
{
if ( random - > nextFloat ( ) < MAX_WEARING_ARMOR_CHANCE * level - > getDifficulty ( x , y , z ) )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
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 - - )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
shared_ptr < ItemInstance > item = getArmor ( i ) ;
if ( i < 3 & & random - > nextFloat ( ) < partialChance ) break ;
if ( item = = NULL )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
Item * equip = getEquipmentForSlot ( i + 1 , armorType ) ;
if ( equip ! = NULL ) setEquippedSlot ( i + 1 , shared_ptr < ItemInstance > ( new ItemInstance ( equip ) ) ) ;
2026-03-01 12:16:08 +08:00
}
}
}
2026-03-03 03:04:10 +08:00
}
int Mob : : getEquipmentSlotForItem ( shared_ptr < ItemInstance > item )
{
if ( item - > id = = Tile : : pumpkin_Id | | item - > id = = Item : : skull_Id )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return SLOT_HELM ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
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 ;
2026-03-01 12:16:08 +08:00
}
}
2026-03-03 03:04:10 +08:00
return SLOT_WEAPON ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
Item * Mob : : getEquipmentForSlot ( int slot , int type )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
switch ( slot )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
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 ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
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 )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
EnchantmentHelper : : enchantItem ( random , item , ( int ) ( 5 + difficulty * random - > nextInt ( 18 ) ) ) ;
2026-03-01 12:16:08 +08:00
}
}
}
2026-03-03 03:04:10 +08:00
/**
* 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
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
// 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));
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
return groupData ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
void Mob : : finalizeSpawnEggSpawn ( int extraData )
{
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
bool Mob : : canBeControlledByRider ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return false ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
wstring Mob : : getAName ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( hasCustomName ( ) ) return getCustomName ( ) ;
return LivingEntity : : getAName ( ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : setPersistenceRequired ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
persistenceRequired = true ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
void Mob : : setCustomName ( const wstring & name )
{
entityData - > set ( DATA_CUSTOM_NAME , name ) ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
wstring Mob : : getCustomName ( )
{
return entityData - > getString ( DATA_CUSTOM_NAME ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
bool Mob : : hasCustomName ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return entityData - > getString ( DATA_CUSTOM_NAME ) . length ( ) > 0 ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
void Mob : : setCustomNameVisible ( bool visible )
{
entityData - > set ( DATA_CUSTOM_NAME_VISIBLE , visible ? ( byte ) 1 : ( byte ) 0 ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
bool Mob : : isCustomNameVisible ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return entityData - > getByte ( DATA_CUSTOM_NAME_VISIBLE ) = = 1 ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
bool Mob : : shouldShowName ( )
{
return isCustomNameVisible ( ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : setDropChance ( int slot , float pct )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
dropChances [ slot ] = pct ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
bool Mob : : canPickUpLoot ( )
{
return _canPickUpLoot ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : setCanPickUpLoot ( bool canPickUpLoot )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
_canPickUpLoot = canPickUpLoot ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
bool Mob : : isPersistenceRequired ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return persistenceRequired ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
bool Mob : : interact ( shared_ptr < Player > player )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( isLeashed ( ) & & getLeashHolder ( ) = = player )
{
dropLeash ( true , ! player - > abilities . instabuild ) ;
return true ;
}
shared_ptr < ItemInstance > itemstack = player - > inventory - > getSelected ( ) ;
if ( itemstack ! = NULL )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
// 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 )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
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 ;
}
}
2026-03-01 12:16:08 +08:00
}
}
2026-03-03 03:04:10 +08:00
if ( mobInteract ( player ) )
{
return true ;
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
return LivingEntity : : interact ( player ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
bool Mob : : mobInteract ( shared_ptr < Player > player )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return false ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : tickLeash ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
if ( leashInfoTag ! = NULL )
{
restoreLeashFromSave ( ) ;
}
if ( ! _isLeashed )
{
return ;
}
if ( leashHolder = = NULL | | leashHolder - > removed )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
dropLeash ( true , true ) ;
return ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
}
void Mob : : dropLeash ( bool synch , bool createItemDrop )
{
if ( _isLeashed )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
_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 ) ) ) ;
}
2026-03-01 12:16:08 +08:00
}
}
2026-03-03 03:04:10 +08:00
bool Mob : : canBeLeashed ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return ! isLeashed ( ) & & ! shared_from_this ( ) - > instanceof ( eTYPE_ENEMY ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
bool Mob : : isLeashed ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return _isLeashed ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
shared_ptr < Entity > Mob : : getLeashHolder ( )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
return leashHolder ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
void Mob : : setLeashedTo ( shared_ptr < Entity > holder , bool synch )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
_isLeashed = true ;
leashHolder = holder ;
ServerLevel * serverLevel = dynamic_cast < ServerLevel * > ( level ) ;
if ( ! level - > isClientSide & & synch & & serverLevel )
{
serverLevel - > getTracker ( ) - > broadcast ( shared_from_this ( ) , shared_ptr < SetEntityLinkPacket > ( new SetEntityLinkPacket ( SetEntityLinkPacket : : LEASH , shared_from_this ( ) , leashHolder ) ) ) ;
}
}
2026-03-01 12:16:08 +08:00
2026-03-03 03:04:10 +08:00
void Mob : : restoreLeashFromSave ( )
{
// after being added to the world, attempt to recreate leash bond
if ( _isLeashed & & leashInfoTag ! = NULL )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
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 ) ;
}
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
leashInfoTag = NULL ;
2026-03-01 12:16:08 +08:00
}
2026-03-03 03:04:10 +08:00
// 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 )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
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 ) ;
2026-03-01 12:16:08 +08:00
}
void Mob : : setLevel ( Level * level )
{
Entity : : setLevel ( level ) ;
navigation - > setLevel ( level ) ;
goalSelector . setLevel ( level ) ;
targetSelector . setLevel ( level ) ;
2026-03-03 03:04:10 +08:00
}