2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "com.mojang.nbt.h"
# include "net.minecraft.world.item.h"
# include "net.minecraft.world.item.enchantment.h"
# include "net.minecraft.world.level.h"
# include "net.minecraft.world.level.tile.h"
# include "net.minecraft.world.phys.h"
# include "net.minecraft.world.entity.item.h"
# include "net.minecraft.world.level.material.h"
# include "net.minecraft.world.damagesource.h"
# include "SynchedEntityData.h"
# include "EntityIO.h"
# include "SharedConstants.h"
# include "ParticleTypes.h"
# include "EntityPos.h"
# include "Entity.h"
# include "SoundTypes.h"
# include "..\minecraft.Client\HumanoidModel.h"
# include "..\Minecraft.Client\MinecraftServer.h"
# include "..\Minecraft.Client\MultiPlayerLevel.h"
# include "..\Minecraft.Client\MultiplayerLocalPlayer.h"
int Entity : : entityCounter = 2048 ; // 4J - changed initialiser to 2048, as we are using range 0 - 2047 as special unique smaller ids for things that need network tracked
DWORD Entity : : tlsIdx = TlsAlloc ( ) ;
// 4J - added getSmallId & freeSmallId methods
unsigned int Entity : : entityIdUsedFlags [ 2048 / 32 ] = { 0 } ;
unsigned int Entity : : entityIdWanderFlags [ 2048 / 32 ] = { 0 } ;
unsigned int Entity : : entityIdRemovingFlags [ 2048 / 32 ] = { 0 } ;
int Entity : : extraWanderIds [ EXTRA_WANDER_MAX ] = { 0 } ;
int Entity : : extraWanderTicks = 0 ;
int Entity : : extraWanderCount = 0 ;
int Entity : : getSmallId ( )
{
unsigned int * puiUsedFlags = entityIdUsedFlags ;
unsigned int * puiRemovedFlags = NULL ;
// If we are the server (we should be, if we are allocating small Ids), then check with the server if there are any small Ids which are
// still in the ServerPlayer's vectors of entities to be removed - these are used to gather up a set of entities into one network packet
// for final notification to the client that the entities are removed. We can't go re-using these small Ids yet, as otherwise we will
// potentially end up telling the client that the entity has been removed After we have already re-used its Id and created a new entity.
// This ends up with newly created client-side entities being removed by accident, causing invisible mobs.
if ( ( ( size_t ) TlsGetValue ( tlsIdx ) ! = 0 ) )
{
MinecraftServer * server = MinecraftServer : : getInstance ( ) ;
if ( server )
{
// In some attempt to optimise this, flagEntitiesToBeRemoved most of the time shouldn't do anything at all, and in this case it
// doesn't even memset the entityIdRemovingFlags array, so we shouldn't use it if the return value is false.
bool removedFound = server - > flagEntitiesToBeRemoved ( entityIdRemovingFlags ) ;
if ( removedFound )
{
// Has set up the entityIdRemovingFlags vector in this case, so we should check against this when allocating new ids
// app.DebugPrintf("getSmallId: Removed entities found\n");
puiRemovedFlags = entityIdRemovingFlags ;
}
}
}
for ( int i = 0 ; i < ( 2048 / 32 ) ; i + + )
{
unsigned int uiFlags = * puiUsedFlags ;
if ( uiFlags ! = 0xffffffff )
{
unsigned int uiMask = 0x80000000 ;
for ( int j = 0 ; j < 32 ; j + + )
{
// See comments above - now checking (if required) that these aren't newly removed entities that the clients still haven't been told about,
// so we don't reuse those ids before we should.
if ( puiRemovedFlags )
{
if ( puiRemovedFlags [ i ] & uiMask )
{
// app.DebugPrintf("Avoiding using ID %d (0x%x)\n", i * 32 + j,puiRemovedFlags[i]);
uiMask > > = 1 ;
continue ;
}
}
if ( ( uiFlags & uiMask ) = = 0 )
{
uiFlags | = uiMask ;
* puiUsedFlags = uiFlags ;
return i * 32 + j ;
}
uiMask > > = 1 ;
}
}
puiUsedFlags + + ;
}
app . DebugPrintf ( " Out of small entity Ids... possible leak? \n " ) ;
__debugbreak ( ) ;
return - 1 ;
}
void Entity : : countFlagsForPIX ( )
{
int freecount = 0 ;
unsigned int * puiUsedFlags = entityIdUsedFlags ;
for ( int i = 0 ; i < ( 2048 / 32 ) ; i + + )
{
unsigned int uiFlags = * puiUsedFlags ;
if ( uiFlags ! = 0xffffffff )
{
unsigned int uiMask = 0x80000000 ;
for ( int j = 0 ; j < 32 ; j + + )
{
if ( ( uiFlags & uiMask ) = = 0 )
{
freecount + + ;
}
uiMask > > = 1 ;
}
}
puiUsedFlags + + ;
}
PIXAddNamedCounter ( freecount , " Small Ids free " ) ;
PIXAddNamedCounter ( 2048 - freecount , " Small Ids used " ) ;
}
void Entity : : resetSmallId ( )
{
freeSmallId ( entityId ) ;
if ( ( ( size_t ) TlsGetValue ( tlsIdx ) ! = 0 ) )
{
entityId = getSmallId ( ) ;
}
}
void Entity : : freeSmallId ( int index )
{
if ( ( ( size_t ) TlsGetValue ( tlsIdx ) ) = = 0 ) return ; // Don't do anything with small ids if this isn't the server thread
if ( index > = 2048 ) return ; // Don't do anything if this isn't a short id
unsigned int i = index / 32 ;
unsigned int j = index % 32 ;
unsigned int uiMask = ~ ( 0x80000000 > > j ) ;
entityIdUsedFlags [ i ] & = uiMask ;
entityIdWanderFlags [ i ] & = uiMask ;
}
void Entity : : useSmallIds ( )
{
TlsSetValue ( tlsIdx , ( LPVOID ) 1 ) ;
}
// Things also added here to be able to manage the concept of a number of extra "wandering" entities - normally path finding entities aren't allowed to
// randomly wander about once they are a certain distance away from any player, but we want to be able to (in a controlled fashion) allow some to be able
// to move so that we can determine whether they have been enclosed in some kind of farm, and so be able to better determine what shouldn't or shouldn't be despawned.
// Let the management system here know whether or not to consider this particular entity for some extra wandering
void Entity : : considerForExtraWandering ( bool enable )
{
if ( ( ( size_t ) TlsGetValue ( tlsIdx ) ) = = 0 ) return ; // Don't do anything with small ids if this isn't the server thread
if ( entityId > = 2048 ) return ; // Don't do anything if this isn't a short id
unsigned int i = entityId / 32 ;
unsigned int j = entityId % 32 ;
if ( enable )
{
unsigned int uiMask = 0x80000000 > > j ;
entityIdWanderFlags [ i ] | = uiMask ;
}
else
{
unsigned int uiMask = ~ ( 0x80000000 > > j ) ;
entityIdWanderFlags [ i ] & = uiMask ;
}
}
// Should this entity do wandering in addition to what the java code would have done?
bool Entity : : isExtraWanderingEnabled ( )
{
if ( ( ( size_t ) TlsGetValue ( tlsIdx ) ) = = 0 ) return false ; // Don't do anything with small ids if this isn't the server thread
if ( entityId > = 2048 ) return false ; // Don't do anything if this isn't a short id
for ( int i = 0 ; i < extraWanderCount ; i + + )
{
if ( extraWanderIds [ i ] = = entityId ) return true ;
}
return false ;
}
// Returns a quadrant of direction that a given entity should be moved in - this is to stop the randomness of the wandering/strolling from just making the entity double back
// on itself and thus making the determination of whether the entity has been enclosed take longer than it needs to. This function returns a quadrant from 0 to 3
// that should be consistent within one period of an entity being considered for extra wandering, but should potentially vary between tries and between different entities.
int Entity : : getWanderingQuadrant ( )
{
return ( entityId + ( extraWanderTicks / EXTRA_WANDER_TICKS ) ) & 3 ;
}
// Every EXTRA_WANDER_TICKS ticks, attempt to find EXTRA_WANDER_MAX entity Ids from those that have been flagged as ones that should be considered for
// extra wandering
void Entity : : tickExtraWandering ( )
{
extraWanderTicks + + ;
// Time to move onto some new entities?
if ( ( extraWanderTicks % EXTRA_WANDER_TICKS = = 0 ) )
{
// printf("Updating extras: ");
// Start from the next Id after the one that we last found, or zero if we didn't find anything last time
int entityId = 0 ;
if ( extraWanderCount )
{
entityId = ( extraWanderIds [ extraWanderCount - 1 ] + 1 ) % 2048 ;
}
extraWanderCount = 0 ;
for ( int k = 0 ; ( k < 2048 ) & & ( extraWanderCount < EXTRA_WANDER_MAX ) ; k + + )
{
unsigned int i = entityId / 32 ;
unsigned int j = entityId % 32 ;
unsigned int uiMask = 0x80000000 > > j ;
if ( entityIdWanderFlags [ i ] & uiMask )
{
extraWanderIds [ extraWanderCount + + ] = entityId ;
// printf("%d, ", entityId);
}
entityId = ( entityId + 1 ) % 2048 ;
}
// printf("\n");
}
}
// 4J - added for common ctor code
// Do all the default initialisations done in the java class
void Entity : : _init ( bool useSmallId )
{
// 4J - changed to assign two different types of ids. A range from 0-2047 is used for things that we'll be wanting to identify over the network,
// so we should only need 11 bits rather than 32 to uniquely identify them. The rest of the range is used for anything we don't need to track like this,
// currently particles. We only ever want to allocate this type of id from the server thread, so using thread local storage to isolate this.
if ( useSmallId & & ( ( size_t ) TlsGetValue ( tlsIdx ) ! = 0 ) )
{
entityId = getSmallId ( ) ;
}
else
{
entityId = Entity : : entityCounter + + ;
if ( entityCounter = = 0x7ffffff ) entityCounter = 2048 ;
}
viewScale = 1.0 ;
blocksBuilding = false ;
rider = weak_ptr < Entity > ( ) ;
riding = nullptr ;
//level = NULL; // Level is assigned to in the original c_tor code
xo = yo = zo = 0.0 ;
x = y = z = 0.0 ;
xd = yd = zd = 0.0 ;
yRot = xRot = 0.0f ;
yRotO = xRotO = 0.0f ;
bb = AABB : : newPermanent ( 0 , 0 , 0 , 0 , 0 , 0 ) ; // 4J Was final
onGround = false ;
horizontalCollision = verticalCollision = false ;
collision = false ;
hurtMarked = false ;
isStuckInWeb = false ;
slide = true ;
removed = false ;
heightOffset = 0 / 16.0f ;
bbWidth = 0.6f ;
bbHeight = 1.8f ;
walkDistO = 0 ;
walkDist = 0 ;
fallDistance = 0 ;
nextStep = 1 ;
xOld = yOld = zOld = 0.0 ;
ySlideOffset = 0 ;
footSize = 0.0f ;
noPhysics = false ;
pushthrough = 0.0f ;
random = new Random ( ) ;
tickCount = 0 ;
flameTime = 1 ;
onFire = 0 ;
wasInWater = false ;
invulnerableTime = 0 ;
firstTick = true ;
customTextureUrl = L " " ;
customTextureUrl2 = L " " ;
fireImmune = false ;
// values that need to be sent to clients in SMP
2026-03-02 15:58:20 +07:00
entityData = std : : shared_ptr < SynchedEntityData > ( new SynchedEntityData ( ) ) ;
2026-03-01 12:16:08 +08:00
xRideRotA = yRideRotA = 0.0 ;
inChunk = false ;
xChunk = yChunk = zChunk = 0 ;
xp = yp = zp = 0 ;
xRotp = yRotp = 0 ;
noCulling = false ;
hasImpulse = false ;
// 4J Added
m_ignoreVerticalCollisions = false ;
m_uiAnimOverrideBitmask = 0L ;
}
Entity : : Entity ( Level * level , bool useSmallId ) // 4J - added useSmallId parameter
{
MemSect ( 16 ) ;
_init ( useSmallId ) ;
MemSect ( 0 ) ;
this - > level = level ;
// resetPos();
setPos ( 0 , 0 , 0 ) ;
entityData - > define ( DATA_SHARED_FLAGS_ID , ( byte ) 0 ) ;
entityData - > define ( DATA_AIR_SUPPLY_ID , TOTAL_AIR_SUPPLY ) ; // 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater.
// 4J Stu - We cannot call virtual functions in ctors, as at this point the object
// is of type Entity and not a derived class
//this->defineSynchedData();
}
Entity : : ~ Entity ( )
{
freeSmallId ( entityId ) ;
delete random ;
delete bb ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < SynchedEntityData > Entity : : getEntityData ( )
2026-03-01 12:16:08 +08:00
{
return entityData ;
}
/*
public bool equals ( Object obj ) {
if ( obj instanceof Entity ) {
return ( ( Entity ) obj ) . entityId = = entityId ;
}
return false ;
}
public int hashCode ( ) {
return entityId ;
}
*/
void Entity : : resetPos ( )
{
if ( level = = NULL ) return ;
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Entity > sharedThis = shared_from_this ( ) ;
2026-03-01 12:16:08 +08:00
while ( true & & y > 0 )
{
setPos ( x , y , z ) ;
if ( level - > getCubes ( sharedThis , bb ) - > empty ( ) ) break ;
y + = 1 ;
}
xd = yd = zd = 0 ;
xRot = 0 ;
}
void Entity : : remove ( )
{
removed = true ;
}
void Entity : : setSize ( float w , float h )
{
2026-03-02 15:58:20 +07:00
if ( w ! = bbWidth | | h ! = bbHeight )
2026-03-01 12:16:08 +08:00
{
float oldW = bbWidth ;
bbWidth = w ;
bbHeight = h ;
bb - > x1 = bb - > x0 + bbWidth ;
bb - > z1 = bb - > z0 + bbWidth ;
bb - > y1 = bb - > y0 + bbHeight ;
2026-03-02 15:58:20 +07:00
if ( bbWidth > oldW & & ! firstTick & & ! level - > isClientSide )
2026-03-01 12:16:08 +08:00
{
move ( oldW - bbWidth , 0 , oldW - bbWidth ) ;
}
}
}
void Entity : : setPos ( EntityPos * pos )
{
if ( pos - > move ) setPos ( pos - > x , pos - > y , pos - > z ) ;
else setPos ( x , y , z ) ;
if ( pos - > rot ) setRot ( pos - > yRot , pos - > xRot ) ;
else setRot ( yRot , xRot ) ;
}
void Entity : : setRot ( float yRot , float xRot )
{
2026-03-02 15:58:20 +07:00
/* JAVA:
2026-03-01 12:16:08 +08:00
this - > yRot = yRot % 360.0f ;
this - > xRot = xRot % 360.0f ;
C + + Cannot do mod of non - integral type
*/
while ( yRot > = 360.0f )
yRot - = 360.0f ;
while ( yRot < 0 )
yRot + = 360.0f ;
while ( xRot > = 360.0f )
xRot - = 360.0f ;
this - > yRot = yRot ;
this - > xRot = xRot ;
}
void Entity : : setPos ( double x , double y , double z )
{
this - > x = x ;
this - > y = y ;
this - > z = z ;
float w = bbWidth / 2 ;
float h = bbHeight ;
bb - > set ( x - w , y - heightOffset + ySlideOffset , z - w , x + w , y - heightOffset + ySlideOffset + h , z + w ) ;
}
void Entity : : turn ( float xo , float yo )
{
float xRotOld = xRot ;
float yRotOld = yRot ;
yRot + = xo * 0.15f ;
xRot - = yo * 0.15f ;
if ( xRot < - 90 ) xRot = - 90 ;
if ( xRot > 90 ) xRot = 90 ;
xRotO + = xRot - xRotOld ;
yRotO + = yRot - yRotOld ;
}
void Entity : : interpolateTurn ( float xo , float yo )
{
yRot + = xo * 0.15f ;
xRot - = yo * 0.15f ;
if ( xRot < - 90 ) xRot = - 90 ;
if ( xRot > 90 ) xRot = 90 ;
}
void Entity : : tick ( )
{
baseTick ( ) ;
}
void Entity : : baseTick ( )
{
// 4J Stu - Not needed
//util.Timer.push("entityBaseTick");
if ( riding ! = NULL & & riding - > removed ) riding = nullptr ;
tickCount + + ;
walkDistO = walkDist ;
xo = x ;
yo = y ;
zo = z ;
xRotO = xRot ;
yRotO = yRot ;
if ( isSprinting ( ) & & ! isInWater ( ) & & canCreateParticles ( ) )
{
int xt = Mth : : floor ( x ) ;
int yt = Mth : : floor ( y - 0.2f - this - > heightOffset ) ;
int zt = Mth : : floor ( z ) ;
int t = level - > getTile ( xt , yt , zt ) ;
int d = level - > getData ( xt , yt , zt ) ;
if ( t > 0 )
{
level - > addParticle ( PARTICLE_TILECRACK ( t , d ) , x + ( random - > nextFloat ( ) - 0.5 ) * bbWidth , bb - > y0 + 0.1 , z + ( random - > nextFloat ( ) - 0.5 ) * bbWidth , - xd * 4 , 1.5 , - zd * 4 ) ;
}
}
if ( updateInWaterState ( ) )
{
if ( ! wasInWater & & ! firstTick & & canCreateParticles ( ) )
{
float speed = Mth : : sqrt ( xd * xd * 0.2f + yd * yd + zd * zd * 0.2f ) * 0.2f ;
if ( speed > 1 ) speed = 1 ;
MemSect ( 31 ) ;
level - > playSound ( shared_from_this ( ) , eSoundType_RANDOM_SPLASH , speed , 1 + ( random - > nextFloat ( ) - random - > nextFloat ( ) ) * 0.4f ) ;
MemSect ( 0 ) ;
float yt = ( float ) Mth : : floor ( bb - > y0 ) ;
for ( int i = 0 ; i < 1 + bbWidth * 20 ; i + + )
{
float xo = ( random - > nextFloat ( ) * 2 - 1 ) * bbWidth ;
float zo = ( random - > nextFloat ( ) * 2 - 1 ) * bbWidth ;
level - > addParticle ( eParticleType_bubble , x + xo , yt + 1 , z + zo , xd , yd - random - > nextFloat ( ) * 0.2f , zd ) ;
}
for ( int i = 0 ; i < 1 + bbWidth * 20 ; i + + )
{
float xo = ( random - > nextFloat ( ) * 2 - 1 ) * bbWidth ;
float zo = ( random - > nextFloat ( ) * 2 - 1 ) * bbWidth ;
level - > addParticle ( eParticleType_splash , x + xo , yt + 1 , z + zo , xd , yd , zd ) ;
}
}
fallDistance = 0 ;
wasInWater = true ;
onFire = 0 ;
2026-03-02 15:58:20 +07:00
}
2026-03-01 12:16:08 +08:00
else
{
wasInWater = false ;
}
if ( level - > isClientSide )
{
onFire = 0 ;
}
2026-03-02 15:58:20 +07:00
else
2026-03-01 12:16:08 +08:00
{
if ( onFire > 0 )
{
if ( fireImmune )
{
onFire - = 4 ;
if ( onFire < 0 ) onFire = 0 ;
}
else
{
if ( onFire % 20 = = 0 )
{
hurt ( DamageSource : : onFire , 1 ) ;
}
onFire - - ;
}
}
}
if ( isInLava ( ) )
{
lavaHurt ( ) ;
fallDistance * = .5f ;
}
if ( y < - 64 )
{
outOfWorld ( ) ;
}
if ( ! level - > isClientSide )
{
setSharedFlag ( FLAG_ONFIRE , onFire > 0 ) ;
setSharedFlag ( FLAG_RIDING , riding ! = NULL ) ;
}
firstTick = false ;
// 4J Stu - Unused
//util.Timer.pop();
}
void Entity : : lavaHurt ( )
{
if ( fireImmune )
{
}
else
{
hurt ( DamageSource : : lava , 4 ) ;
setOnFire ( 15 ) ;
}
}
void Entity : : setOnFire ( int numberOfSeconds )
{
int newValue = numberOfSeconds * SharedConstants : : TICKS_PER_SECOND ;
newValue = ProtectionEnchantment : : getFireAfterDampener ( shared_from_this ( ) , newValue ) ;
if ( onFire < newValue )
{
onFire = newValue ;
}
}
void Entity : : clearFire ( )
{
onFire = 0 ;
}
void Entity : : outOfWorld ( )
{
remove ( ) ;
}
bool Entity : : isFree ( float xa , float ya , float za , float grow )
{
AABB * box = bb - > grow ( grow , grow , grow ) - > cloneMove ( xa , ya , za ) ;
AABBList * aABBs = level - > getCubes ( shared_from_this ( ) , box ) ;
if ( ! aABBs - > empty ( ) ) return false ;
if ( level - > containsAnyLiquid ( box ) ) return false ;
return true ;
}
bool Entity : : isFree ( double xa , double ya , double za )
{
AABB * box = bb - > cloneMove ( xa , ya , za ) ;
AABBList * aABBs = level - > getCubes ( shared_from_this ( ) , box ) ;
if ( ! aABBs - > empty ( ) ) return false ;
if ( level - > containsAnyLiquid ( box ) ) return false ;
return true ;
}
void Entity : : move ( double xa , double ya , double za , bool noEntityCubes ) // 4J - added noEntityCubes parameter
{
if ( noPhysics )
{
bb - > move ( xa , ya , za ) ;
x = ( bb - > x0 + bb - > x1 ) / 2.0f ;
y = bb - > y0 + heightOffset - ySlideOffset ;
z = ( bb - > z0 + bb - > z1 ) / 2.0f ;
return ;
}
ySlideOffset * = 0.4f ;
double xo = x ;
double zo = z ;
if ( isStuckInWeb )
{
isStuckInWeb = false ;
xa * = 0.25f ;
ya * = 0.05f ;
za * = 0.25f ;
xd = 0.0f ;
yd = 0.0f ;
zd = 0.0f ;
}
double xaOrg = xa ;
double yaOrg = ya ;
double zaOrg = za ;
AABB * bbOrg = bb - > copy ( ) ;
2026-03-02 17:10:34 +07:00
bool isPlayerSneaking = onGround & & isSneaking ( ) & & std : : dynamic_pointer_cast < Player > ( shared_from_this ( ) ) ! = NULL ;
2026-03-01 12:16:08 +08:00
if ( isPlayerSneaking )
{
double d = 0.05 ;
while ( xa ! = 0 & & level - > getCubes ( shared_from_this ( ) , bb - > cloneMove ( xa , - 1.0 , 0 ) ) - > empty ( ) )
{
if ( xa < d & & xa > = - d ) xa = 0 ;
else if ( xa > 0 ) xa - = d ;
else xa + = d ;
xaOrg = xa ;
}
while ( za ! = 0 & & level - > getCubes ( shared_from_this ( ) , bb - > cloneMove ( 0 , - 1.0 , za ) ) - > empty ( ) )
{
if ( za < d & & za > = - d ) za = 0 ;
else if ( za > 0 ) za - = d ;
else za + = d ;
zaOrg = za ;
}
while ( xa ! = 0 & & za ! = 0 & & level - > getCubes ( shared_from_this ( ) , bb - > cloneMove ( xa , - 1.0 , za ) ) - > empty ( ) )
{
if ( xa < d & & xa > = - d ) xa = 0 ;
else if ( xa > 0 ) xa - = d ;
else xa + = d ;
if ( za < d & & za > = - d ) za = 0 ;
else if ( za > 0 ) za - = d ;
else za + = d ;
xaOrg = xa ;
zaOrg = za ;
}
}
AABBList * aABBs = level - > getCubes ( shared_from_this ( ) , bb - > expand ( xa , ya , za ) , noEntityCubes , true ) ;
// LAND FIRST, then x and z
AUTO_VAR ( itEndAABB , aABBs - > end ( ) ) ;
// 4J Stu - Particles (and possibly other entities) don't have xChunk and zChunk set, so calculate the chunk instead
int xc = Mth : : floor ( x / 16 ) ;
int zc = Mth : : floor ( z / 16 ) ;
if ( ! level - > isClientSide | | level - > reallyHasChunk ( xc , zc ) )
{
// 4J Stu - It's horrible that the client is doing any movement at all! But if we don't have the chunk
// data then all the collision info will be incorrect as well
for ( AUTO_VAR ( it , aABBs - > begin ( ) ) ; it ! = itEndAABB ; it + + )
ya = ( * it ) - > clipYCollide ( bb , ya ) ;
bb - > move ( 0 , ya , 0 ) ;
}
if ( ! slide & & yaOrg ! = ya )
{
xa = ya = za = 0 ;
}
bool og = onGround | | ( yaOrg ! = ya & & yaOrg < 0 ) ;
itEndAABB = aABBs - > end ( ) ;
for ( AUTO_VAR ( it , aABBs - > begin ( ) ) ; it ! = itEndAABB ; it + + )
xa = ( * it ) - > clipXCollide ( bb , xa ) ;
bb - > move ( xa , 0 , 0 ) ;
if ( ! slide & & xaOrg ! = xa )
{
xa = ya = za = 0 ;
}
itEndAABB = aABBs - > end ( ) ;
for ( AUTO_VAR ( it , aABBs - > begin ( ) ) ; it ! = itEndAABB ; it + + )
za = ( * it ) - > clipZCollide ( bb , za ) ;
bb - > move ( 0 , 0 , za ) ;
if ( ! slide & & zaOrg ! = za )
{
xa = ya = za = 0 ;
}
if ( footSize > 0 & & og & & ( isPlayerSneaking | | ySlideOffset < 0.05f ) & & ( ( xaOrg ! = xa ) | | ( zaOrg ! = za ) ) )
{
double xaN = xa ;
double yaN = ya ;
double zaN = za ;
xa = xaOrg ;
ya = footSize ;
za = zaOrg ;
AABB * normal = bb - > copy ( ) ;
bb - > set ( bbOrg ) ;
// 4J - added extra expand, as if we don't move up by footSize by hitting a block above us, then overall we could be trying to move as much as footSize downwards,
// so we'd better include cubes under our feet in this list of things we might possibly collide with
aABBs = level - > getCubes ( shared_from_this ( ) , bb - > expand ( xa , ya , za ) - > expand ( 0 , - ya , 0 ) , false , true ) ;
// LAND FIRST, then x and z
itEndAABB = aABBs - > end ( ) ;
if ( ! level - > isClientSide | | level - > reallyHasChunk ( xc , zc ) )
{
// 4J Stu - It's horrible that the client is doing any movement at all! But if we don't have the chunk
// data then all the collision info will be incorrect as well
for ( AUTO_VAR ( it , aABBs - > begin ( ) ) ; it ! = itEndAABB ; it + + )
ya = ( * it ) - > clipYCollide ( bb , ya ) ;
bb - > move ( 0 , ya , 0 ) ;
}
if ( ! slide & & yaOrg ! = ya )
{
xa = ya = za = 0 ;
}
itEndAABB = aABBs - > end ( ) ;
for ( AUTO_VAR ( it , aABBs - > begin ( ) ) ; it ! = itEndAABB ; it + + )
xa = ( * it ) - > clipXCollide ( bb , xa ) ;
bb - > move ( xa , 0 , 0 ) ;
if ( ! slide & & xaOrg ! = xa )
{
xa = ya = za = 0 ;
}
itEndAABB = aABBs - > end ( ) ;
for ( AUTO_VAR ( it , aABBs - > begin ( ) ) ; it ! = itEndAABB ; it + + )
za = ( * it ) - > clipZCollide ( bb , za ) ;
bb - > move ( 0 , 0 , za ) ;
if ( ! slide & & zaOrg ! = za )
{
xa = ya = za = 0 ;
}
if ( ! slide & & yaOrg ! = ya )
{
xa = ya = za = 0 ;
}
else
{
ya = - footSize ;
// LAND FIRST, then x and z
itEndAABB = aABBs - > end ( ) ;
for ( AUTO_VAR ( it , aABBs - > begin ( ) ) ; it ! = itEndAABB ; it + + )
ya = ( * it ) - > clipYCollide ( bb , ya ) ;
bb - > move ( 0 , ya , 0 ) ;
}
if ( xaN * xaN + zaN * zaN > = xa * xa + za * za )
{
xa = xaN ;
ya = yaN ;
za = zaN ;
bb - > set ( normal ) ;
}
else
{
double ss = bb - > y0 - ( int ) bb - > y0 ;
if ( ss > 0 ) {
ySlideOffset + = ( float ) ( ss + 0.01 ) ;
}
}
}
x = ( bb - > x0 + bb - > x1 ) / 2.0f ;
y = bb - > y0 + heightOffset - ySlideOffset ;
z = ( bb - > z0 + bb - > z1 ) / 2.0f ;
horizontalCollision = ( xaOrg ! = xa ) | | ( zaOrg ! = za ) ;
verticalCollision = ! m_ignoreVerticalCollisions & & ( yaOrg ! = ya ) ;
onGround = ! m_ignoreVerticalCollisions & & yaOrg ! = ya & & yaOrg < 0 ;
collision = horizontalCollision | | verticalCollision ;
checkFallDamage ( ya , onGround ) ;
if ( xaOrg ! = xa ) xd = 0 ;
if ( yaOrg ! = ya ) yd = 0 ;
if ( zaOrg ! = za ) zd = 0 ;
double xm = x - xo ;
double zm = z - zo ;
if ( makeStepSound ( ) & & ! isPlayerSneaking & & riding = = NULL )
{
walkDist + = ( float ) ( sqrt ( xm * xm + zm * zm ) * 0.6 ) ;
int xt = Mth : : floor ( x ) ;
int yt = Mth : : floor ( y - 0.2f - this - > heightOffset ) ;
int zt = Mth : : floor ( z ) ;
int t = level - > getTile ( xt , yt , zt ) ;
if ( t = = 0 )
{
int renderShape = level - > getTileRenderShape ( xt , yt - 1 , zt ) ;
if ( renderShape = = Tile : : SHAPE_FENCE | | renderShape = = Tile : : SHAPE_WALL | | renderShape = = Tile : : SHAPE_FENCE_GATE )
{
t = level - > getTile ( xt , yt - 1 , zt ) ;
}
}
if ( walkDist > nextStep & & t > 0 )
{
nextStep = ( int ) walkDist + 1 ;
playStepSound ( xt , yt , zt , t ) ;
Tile : : tiles [ t ] - > stepOn ( level , xt , yt , zt , shared_from_this ( ) ) ;
}
}
checkInsideTiles ( ) ;
bool water = this - > isInWaterOrRain ( ) ;
if ( level - > containsFireTile ( bb - > shrink ( 0.001 , 0.001 , 0.001 ) ) )
{
burn ( 1 ) ;
if ( ! water )
{
onFire + + ;
if ( onFire = = 0 ) setOnFire ( 8 ) ;
}
}
else
{
if ( onFire < = 0 )
{
onFire = - flameTime ;
}
}
if ( water & & onFire > 0 )
{
level - > playSound ( shared_from_this ( ) , eSoundType_RANDOM_FIZZ , 0.7f , 1.6f + ( random - > nextFloat ( ) - random - > nextFloat ( ) ) * 0.4f ) ;
onFire = - flameTime ;
}
}
void Entity : : checkInsideTiles ( )
{
int x0 = Mth : : floor ( bb - > x0 + 0.001 ) ;
int y0 = Mth : : floor ( bb - > y0 + 0.001 ) ;
int z0 = Mth : : floor ( bb - > z0 + 0.001 ) ;
int x1 = Mth : : floor ( bb - > x1 - 0.001 ) ;
int y1 = Mth : : floor ( bb - > y1 - 0.001 ) ;
int z1 = Mth : : floor ( bb - > z1 - 0.001 ) ;
if ( level - > hasChunksAt ( x0 , y0 , z0 , x1 , y1 , z1 ) )
{
for ( int x = x0 ; x < = x1 ; x + + )
for ( int y = y0 ; y < = y1 ; y + + )
for ( int z = z0 ; z < = z1 ; z + + )
{
int t = level - > getTile ( x , y , z ) ;
if ( t > 0 )
{
Tile : : tiles [ t ] - > entityInside ( level , x , y , z , shared_from_this ( ) ) ;
}
}
}
}
void Entity : : playStepSound ( int xt , int yt , int zt , int t )
{
const Tile : : SoundType * soundType = Tile : : tiles [ t ] - > soundType ;
MemSect ( 31 ) ;
if ( GetType ( ) = = eTYPE_PLAYER )
{
// should we turn off step sounds?
unsigned int uiAnimOverrideBitmask = getAnimOverrideBitmask ( ) ; // this is masked for custom anim off, and force anim
if ( ( uiAnimOverrideBitmask & ( 1 < < HumanoidModel : : eAnim_NoLegAnim ) ) ! = 0 )
{
return ;
}
MultiPlayerLevel * mplevel = ( MultiPlayerLevel * ) level ;
if ( mplevel )
{
if ( level - > getTile ( xt , yt + 1 , zt ) = = Tile : : topSnow_Id )
{
soundType = Tile : : topSnow - > soundType ;
mplevel - > playLocalSound ( ( double ) xt + 0.5 , ( double ) yt , ( double ) zt + 0.5 , soundType - > getStepSound ( ) , soundType - > getVolume ( ) * 0.15f , soundType - > getPitch ( ) ) ;
}
else if ( ! Tile : : tiles [ t ] - > material - > isLiquid ( ) )
{
mplevel - > playLocalSound ( ( double ) xt + 0.5 , ( double ) yt , ( double ) zt + 0.5 , soundType - > getStepSound ( ) , soundType - > getVolume ( ) * 0.15f , soundType - > getPitch ( ) ) ;
}
}
else
2026-03-02 15:58:20 +07:00
{
2026-03-01 12:16:08 +08:00
if ( level - > getTile ( xt , yt + 1 , zt ) = = Tile : : topSnow_Id )
{
soundType = Tile : : topSnow - > soundType ;
level - > playLocalSound ( ( double ) xt + 0.5 , ( double ) yt , ( double ) zt + 0.5 , soundType - > getStepSound ( ) , soundType - > getVolume ( ) * 0.15f , soundType - > getPitch ( ) ) ;
}
else if ( ! Tile : : tiles [ t ] - > material - > isLiquid ( ) )
{
level - > playLocalSound ( ( double ) xt + 0.5 , ( double ) yt , ( double ) zt + 0.5 , soundType - > getStepSound ( ) , soundType - > getVolume ( ) * 0.15f , soundType - > getPitch ( ) ) ;
}
}
}
else
{
if ( level - > getTile ( xt , yt + 1 , zt ) = = Tile : : topSnow_Id )
{
soundType = Tile : : topSnow - > soundType ;
level - > playSound ( shared_from_this ( ) , soundType - > getStepSound ( ) , soundType - > getVolume ( ) * 0.15f , soundType - > getPitch ( ) ) ;
}
else if ( ! Tile : : tiles [ t ] - > material - > isLiquid ( ) )
{
level - > playSound ( shared_from_this ( ) , soundType - > getStepSound ( ) , soundType - > getVolume ( ) * 0.15f , soundType - > getPitch ( ) ) ;
}
}
MemSect ( 0 ) ;
}
void Entity : : playSound ( int iSound , float volume , float pitch )
{
level - > playSound ( shared_from_this ( ) , iSound , volume , pitch ) ;
}
bool Entity : : makeStepSound ( )
{
return true ;
}
void Entity : : checkFallDamage ( double ya , bool onGround )
{
if ( onGround )
{
if ( fallDistance > 0 )
{
2026-03-02 17:10:34 +07:00
if ( std : : dynamic_pointer_cast < Mob > ( shared_from_this ( ) ) ! = NULL )
2026-03-01 12:16:08 +08:00
{
int xt = Mth : : floor ( x ) ;
int yt = Mth : : floor ( y - 0.2f - this - > heightOffset ) ;
int zt = Mth : : floor ( z ) ;
int t = level - > getTile ( xt , yt , zt ) ;
if ( t = = 0 & & level - > getTile ( xt , yt - 1 , zt ) = = Tile : : fence_Id )
{
t = level - > getTile ( xt , yt - 1 , zt ) ;
}
if ( t > 0 )
{
Tile : : tiles [ t ] - > fallOn ( level , xt , yt , zt , shared_from_this ( ) , fallDistance ) ;
}
}
causeFallDamage ( fallDistance ) ;
fallDistance = 0 ;
}
2026-03-02 15:58:20 +07:00
}
2026-03-01 12:16:08 +08:00
else
{
if ( ya < 0 ) fallDistance - = ( float ) ya ;
}
}
AABB * Entity : : getCollideBox ( )
{
return NULL ;
}
void Entity : : burn ( int dmg )
{
if ( ! fireImmune )
{
hurt ( DamageSource : : inFire , dmg ) ;
}
}
bool Entity : : isFireImmune ( )
{
return fireImmune ;
}
void Entity : : causeFallDamage ( float distance )
{
if ( rider . lock ( ) ! = NULL ) rider . lock ( ) - > causeFallDamage ( distance ) ;
}
bool Entity : : isInWaterOrRain ( )
{
return wasInWater | | ( level - > isRainingAt ( Mth : : floor ( x ) , Mth : : floor ( y ) , Mth : : floor ( z ) ) ) ;
}
bool Entity : : isInWater ( )
{
return wasInWater ;
}
bool Entity : : updateInWaterState ( )
{
return level - > checkAndHandleWater ( bb - > grow ( 0 , - 0.4f , 0 ) - > shrink ( 0.001 , 0.001 , 0.001 ) , Material : : water , shared_from_this ( ) ) ;
}
bool Entity : : isUnderLiquid ( Material * material )
{
double yp = y + getHeadHeight ( ) ;
int xt = Mth : : floor ( x ) ;
int yt = Mth : : floor ( yp ) ; // 4J - this used to be a nested pair of floors for some reason
int zt = Mth : : floor ( z ) ;
int t = level - > getTile ( xt , yt , zt ) ;
if ( t ! = 0 & & Tile : : tiles [ t ] - > material = = material ) {
float hh = LiquidTile : : getHeight ( level - > getData ( xt , yt , zt ) ) - 1 / 9.0f ;
float h = yt + 1 - hh ;
return yp < h ;
}
return false ;
}
float Entity : : getHeadHeight ( )
{
return 0 ;
}
bool Entity : : isInLava ( )
{
return level - > containsMaterial ( bb - > grow ( - 0.1f , - 0.4f , - 0.1f ) , Material : : lava ) ;
}
void Entity : : moveRelative ( float xa , float za , float speed )
{
float dist = xa * xa + za * za ;
if ( dist < 0.01f * 0.01f ) return ;
dist = sqrt ( dist ) ;
if ( dist < 1 ) dist = 1 ;
dist = speed / dist ;
xa * = dist ;
za * = dist ;
float sinVar = Mth : : sin ( yRot * PI / 180 ) ;
float cosVar = Mth : : cos ( yRot * PI / 180 ) ;
xd + = xa * cosVar - za * sinVar ;
zd + = za * cosVar + xa * sinVar ;
}
// 4J - change brought forward from 1.8.2
int Entity : : getLightColor ( float a )
{
int xTile = Mth : : floor ( x ) ;
int zTile = Mth : : floor ( z ) ;
if ( level - > hasChunkAt ( xTile , 0 , zTile ) )
{
double hh = ( bb - > y1 - bb - > y0 ) * 0.66 ;
int yTile = Mth : : floor ( y - this - > heightOffset + hh ) ;
return level - > getLightColor ( xTile , yTile , zTile , 0 ) ;
}
return 0 ;
}
// 4J - changes brought forward from 1.8.2
float Entity : : getBrightness ( float a )
{
int xTile = Mth : : floor ( x ) ;
int zTile = Mth : : floor ( z ) ;
if ( level - > hasChunkAt ( xTile , 0 , zTile ) )
{
double hh = ( bb - > y1 - bb - > y0 ) * 0.66 ;
int yTile = Mth : : floor ( y - this - > heightOffset + hh ) ;
return level - > getBrightness ( xTile , yTile , zTile ) ;
}
return 0 ;
}
void Entity : : setLevel ( Level * level )
{
this - > level = level ;
}
void Entity : : absMoveTo ( double x , double y , double z , float yRot , float xRot )
{
this - > xo = this - > x = x ;
this - > yo = this - > y = y ;
this - > zo = this - > z = z ;
this - > yRotO = this - > yRot = yRot ;
this - > xRotO = this - > xRot = xRot ;
ySlideOffset = 0 ;
double yRotDiff = yRotO - yRot ;
if ( yRotDiff < - 180 ) yRotO + = 360 ;
if ( yRotDiff > = 180 ) yRotO - = 360 ;
this - > setPos ( this - > x , this - > y , this - > z ) ;
this - > setRot ( yRot , xRot ) ;
}
void Entity : : moveTo ( double x , double y , double z , float yRot , float xRot )
{
this - > xOld = this - > xo = this - > x = x ;
this - > yOld = this - > yo = this - > y = y + heightOffset ;
this - > zOld = this - > zo = this - > z = z ;
this - > yRot = yRot ;
this - > xRot = xRot ;
this - > setPos ( this - > x , this - > y , this - > z ) ;
}
2026-03-02 15:58:20 +07:00
float Entity : : distanceTo ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
float xd = ( float ) ( x - e - > x ) ;
float yd = ( float ) ( y - e - > y ) ;
float zd = ( float ) ( z - e - > z ) ;
return sqrt ( xd * xd + yd * yd + zd * zd ) ;
}
double Entity : : distanceToSqr ( double x2 , double y2 , double z2 )
{
double xd = ( x - x2 ) ;
double yd = ( y - y2 ) ;
double zd = ( z - z2 ) ;
return xd * xd + yd * yd + zd * zd ;
}
double Entity : : distanceTo ( double x2 , double y2 , double z2 )
{
double xd = ( x - x2 ) ;
double yd = ( y - y2 ) ;
double zd = ( z - z2 ) ;
return sqrt ( xd * xd + yd * yd + zd * zd ) ;
}
2026-03-02 15:58:20 +07:00
double Entity : : distanceToSqr ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
double xd = x - e - > x ;
double yd = y - e - > y ;
double zd = z - e - > z ;
return xd * xd + yd * yd + zd * zd ;
}
2026-03-02 15:58:20 +07:00
void Entity : : playerTouch ( std : : shared_ptr < Player > player )
2026-03-01 12:16:08 +08:00
{
}
2026-03-02 15:58:20 +07:00
void Entity : : push ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
if ( e - > rider . lock ( ) . get ( ) = = this | | e - > riding . get ( ) = = this ) return ;
double xa = e - > x - x ;
double za = e - > z - z ;
double dd = Mth : : asbMax ( xa , za ) ;
if ( dd > = 0.01f )
{
dd = sqrt ( dd ) ;
xa / = dd ;
za / = dd ;
double pow = 1 / dd ;
if ( pow > 1 ) pow = 1 ;
xa * = pow ;
za * = pow ;
xa * = 0.05f ;
za * = 0.05f ;
xa * = 1 - pushthrough ;
za * = 1 - pushthrough ;
this - > push ( - xa , 0 , - za ) ;
e - > push ( xa , 0 , za ) ;
}
}
void Entity : : push ( double xa , double ya , double za )
{
xd + = xa ;
yd + = ya ;
zd + = za ;
this - > hasImpulse = true ;
}
void Entity : : markHurt ( )
{
this - > hurtMarked = true ;
}
bool Entity : : hurt ( DamageSource * source , int damage )
{
markHurt ( ) ;
return false ;
}
bool Entity : : intersects ( double x0 , double y0 , double z0 , double x1 , double y1 , double z1 )
{
return bb - > intersects ( x0 , y0 , z0 , x1 , y1 , z1 ) ;
}
bool Entity : : isPickable ( )
{
return false ;
}
bool Entity : : isPushable ( )
{
return false ;
}
bool Entity : : isShootable ( )
{
return false ;
}
2026-03-02 15:58:20 +07:00
void Entity : : awardKillScore ( std : : shared_ptr < Entity > victim , int score )
2026-03-01 12:16:08 +08:00
{
}
bool Entity : : shouldRender ( Vec3 * c )
{
double xd = x - c - > x ;
double yd = y - c - > y ;
double zd = z - c - > z ;
double distance = xd * xd + yd * yd + zd * zd ;
return shouldRenderAtSqrDistance ( distance ) ;
}
bool Entity : : shouldRenderAtSqrDistance ( double distance )
{
double size = bb - > getSize ( ) ;
size * = 64.0f * viewScale ;
return distance < size * size ;
}
// 4J - used to be wstring return type, returning L""
int Entity : : getTexture ( )
{
return - 1 ;
}
bool Entity : : isCreativeModeAllowed ( )
{
return false ;
}
bool Entity : : save ( CompoundTag * entityTag )
{
wstring id = getEncodeId ( ) ;
if ( removed | | id . empty ( ) )
{
return false ;
}
// TODO Is this fine to be casting to a non-const char pointer?
entityTag - > putString ( L " id " , id ) ;
saveWithoutId ( entityTag ) ;
return true ;
}
void Entity : : saveWithoutId ( CompoundTag * entityTag )
{
entityTag - > put ( L " Pos " , newDoubleList ( 3 , x , y + ySlideOffset , z ) ) ;
entityTag - > put ( L " Motion " , newDoubleList ( 3 , xd , yd , zd ) ) ;
entityTag - > put ( L " Rotation " , newFloatList ( 2 , yRot , xRot ) ) ;
entityTag - > putFloat ( L " FallDistance " , fallDistance ) ;
entityTag - > putShort ( L " Fire " , ( short ) onFire ) ;
entityTag - > putShort ( L " Air " , ( short ) getAirSupply ( ) ) ;
entityTag - > putBoolean ( L " OnGround " , onGround ) ;
addAdditonalSaveData ( entityTag ) ;
}
void Entity : : load ( CompoundTag * tag )
{
ListTag < DoubleTag > * pos = ( ListTag < DoubleTag > * ) tag - > getList ( L " Pos " ) ;
ListTag < DoubleTag > * motion = ( ListTag < DoubleTag > * ) tag - > getList ( L " Motion " ) ;
ListTag < FloatTag > * rotation = ( ListTag < FloatTag > * ) tag - > getList ( L " Rotation " ) ;
xd = motion - > get ( 0 ) - > data ;
yd = motion - > get ( 1 ) - > data ;
zd = motion - > get ( 2 ) - > data ;
if ( abs ( xd ) > 10.0 )
{
xd = 0 ;
}
if ( abs ( yd ) > 10.0 )
{
yd = 0 ;
}
if ( abs ( zd ) > 10.0 )
{
zd = 0 ;
}
xo = xOld = x = pos - > get ( 0 ) - > data ;
yo = yOld = y = pos - > get ( 1 ) - > data ;
zo = zOld = z = pos - > get ( 2 ) - > data ;
yRotO = yRot = rotation - > get ( 0 ) - > data ;
xRotO = xRot = rotation - > get ( 1 ) - > data ;
fallDistance = tag - > getFloat ( L " FallDistance " ) ;
onFire = tag - > getShort ( L " Fire " ) ;
setAirSupply ( tag - > getShort ( L " Air " ) ) ;
onGround = tag - > getBoolean ( L " OnGround " ) ;
setPos ( x , y , z ) ;
setRot ( yRot , xRot ) ;
readAdditionalSaveData ( tag ) ;
}
const wstring Entity : : getEncodeId ( )
{
return EntityIO : : getEncodeId ( shared_from_this ( ) ) ;
}
ListTag < DoubleTag > * Entity : : newDoubleList ( unsigned int number , double firstValue , . . . )
{
ListTag < DoubleTag > * res = new ListTag < DoubleTag > ( ) ;
// Add the first parameter to the ListTag
res - > add ( new DoubleTag ( L " " , firstValue ) ) ;
va_list vl ;
va_start ( vl , firstValue ) ;
double val ;
for ( unsigned int i = 1 ; i < number ; i + + )
{
val = va_arg ( vl , double ) ;
res - > add ( new DoubleTag ( L " " , val ) ) ;
}
va_end ( vl ) ;
return res ;
}
ListTag < FloatTag > * Entity : : newFloatList ( unsigned int number , float firstValue , float secondValue )
{
ListTag < FloatTag > * res = new ListTag < FloatTag > ( ) ;
// Add the first parameter to the ListTag
res - > add ( new FloatTag ( L " " , firstValue ) ) ;
// TODO - 4J Stu For some reason the va_list wasn't working correctly here
// We only make a list of two floats so just overriding and not using va_list
res - > add ( new FloatTag ( L " " , secondValue ) ) ;
/*
va_list vl ;
va_start ( vl , firstValue ) ;
float val ;
for ( unsigned int i = 1 ; i < number ; i + + )
{
val = va_arg ( vl , float ) ;
res - > add ( new FloatTag ( val ) ) ;
}
va_end ( vl ) ;
*/
return res ;
}
float Entity : : getShadowHeightOffs ( )
{
return bbHeight / 2 ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemEntity > Entity : : spawnAtLocation ( int resource , int count )
2026-03-01 12:16:08 +08:00
{
return spawnAtLocation ( resource , count , 0 ) ;
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemEntity > Entity : : spawnAtLocation ( int resource , int count , float yOffs )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
return spawnAtLocation ( std : : shared_ptr < ItemInstance > ( new ItemInstance ( resource , count , 0 ) ) , yOffs ) ;
2026-03-01 12:16:08 +08:00
}
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemEntity > Entity : : spawnAtLocation ( std : : shared_ptr < ItemInstance > itemInstance , float yOffs )
2026-03-01 12:16:08 +08:00
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ItemEntity > ie = std : : shared_ptr < ItemEntity > ( new ItemEntity ( level , x , y + yOffs , z , itemInstance ) ) ;
2026-03-01 12:16:08 +08:00
ie - > throwTime = 10 ;
level - > addEntity ( ie ) ;
return ie ;
}
bool Entity : : isAlive ( )
{
return ! removed ;
}
bool Entity : : isInWall ( )
{
for ( int i = 0 ; i < 8 ; i + + )
{
float xo = ( ( i > > 0 ) % 2 - 0.5f ) * bbWidth * 0.8f ;
float yo = ( ( i > > 1 ) % 2 - 0.5f ) * 0.1f ;
float zo = ( ( i > > 2 ) % 2 - 0.5f ) * bbWidth * 0.8f ;
int xt = Mth : : floor ( x + xo ) ;
int yt = Mth : : floor ( y + this - > getHeadHeight ( ) + yo ) ;
int zt = Mth : : floor ( z + zo ) ;
if ( level - > isSolidBlockingTile ( xt , yt , zt ) )
{
return true ;
}
}
return false ;
}
2026-03-02 15:58:20 +07:00
bool Entity : : interact ( std : : shared_ptr < Player > player )
2026-03-01 12:16:08 +08:00
{
return false ;
}
2026-03-02 15:58:20 +07:00
AABB * Entity : : getCollideAgainstBox ( std : : shared_ptr < Entity > entity )
2026-03-01 12:16:08 +08:00
{
return NULL ;
}
void Entity : : rideTick ( )
{
if ( riding - > removed )
{
riding = nullptr ;
return ;
}
xd = yd = zd = 0 ;
tick ( ) ;
if ( riding = = NULL ) return ;
// Sets riders old&new position to it's mount's old&new position (plus the ride y-seperatation).
riding - > positionRider ( ) ;
yRideRotA + = ( riding - > yRot - riding - > yRotO ) ;
xRideRotA + = ( riding - > xRot - riding - > xRotO ) ;
// Wrap rotation angles.
while ( yRideRotA > = 180 ) yRideRotA - = 360 ;
while ( yRideRotA < - 180 ) yRideRotA + = 360 ;
while ( xRideRotA > = 180 ) xRideRotA - = 360 ;
while ( xRideRotA < - 180 ) xRideRotA + = 360 ;
double yra = yRideRotA * 0.5 ;
double xra = xRideRotA * 0.5 ;
// Cap rotation speed.
float max = 10 ;
if ( yra > max ) yra = max ;
if ( yra < - max ) yra = - max ;
if ( xra > max ) xra = max ;
if ( xra < - max ) xra = - max ;
yRideRotA - = yra ;
xRideRotA - = xra ;
yRot + = ( float ) yra ;
xRot + = ( float ) xra ;
}
void Entity : : positionRider ( )
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < Entity > lockedRider = rider . lock ( ) ;
2026-03-01 12:16:08 +08:00
if ( lockedRider )
{
2026-03-02 17:10:34 +07:00
std : : shared_ptr < Player > player = std : : dynamic_pointer_cast < Player > ( lockedRider ) ;
2026-03-01 12:16:08 +08:00
if ( ! ( player & & player - > isLocalPlayer ( ) ) )
{
lockedRider - > xOld = xOld ;
lockedRider - > yOld = yOld + getRideHeight ( ) + lockedRider - > getRidingHeight ( ) ;
lockedRider - > zOld = zOld ;
}
lockedRider - > setPos ( x , y + getRideHeight ( ) + lockedRider - > getRidingHeight ( ) , z ) ;
}
}
double Entity : : getRidingHeight ( )
{
return heightOffset ;
}
double Entity : : getRideHeight ( )
{
return bbHeight * .75 ;
}
2026-03-02 15:58:20 +07:00
void Entity : : ride ( std : : shared_ptr < Entity > e )
2026-03-01 12:16:08 +08:00
{
xRideRotA = 0 ;
yRideRotA = 0 ;
if ( e = = NULL )
{
if ( riding ! = NULL )
{
// 4J Stu - Position should already be updated before the SetRidingPacket comes in
if ( ! level - > isClientSide ) moveTo ( riding - > x , riding - > bb - > y0 + riding - > bbHeight , riding - > z , yRot , xRot ) ;
riding - > rider = weak_ptr < Entity > ( ) ;
}
riding = nullptr ;
return ;
}
if ( riding ! = NULL )
{
riding - > rider = weak_ptr < Entity > ( ) ;
}
riding = e ;
e - > rider = shared_from_this ( ) ;
}
// 4J Stu - Brought forward from 12w36 to fix #46282 - TU5: Gameplay: Exiting the minecart in a tight corridor damages the player
2026-03-02 15:58:20 +07:00
void Entity : : findStandUpPosition ( std : : shared_ptr < Entity > vehicle )
2026-03-01 12:16:08 +08:00
{
AABB * boundingBox ;
double fallbackX = vehicle - > x ;
double fallbackY = vehicle - > bb - > y0 + vehicle - > bbHeight ;
double fallbackZ = vehicle - > z ;
for ( double xDiff = - 1.5 ; xDiff < 2 ; xDiff + = 1.5 )
{
for ( double zDiff = - 1.5 ; zDiff < 2 ; zDiff + = 1.5 )
{
if ( xDiff = = 0 & & zDiff = = 0 )
{
continue ;
}
int xToInt = ( int ) ( this - > x + xDiff ) ;
int zToInt = ( int ) ( this - > z + zDiff ) ;
// 4J Stu - Added loop over y to restaring the bb into 2 block high spaces if required (eg the track block plus 1 air block above it for minecarts)
for ( double yDiff = 1.0 ; yDiff > = 0 ; yDiff - = 0.5 )
{
boundingBox = this - > bb - > cloneMove ( xDiff , yDiff , zDiff ) ;
if ( level - > getTileCubes ( boundingBox , true ) - > size ( ) = = 0 )
{
if ( level - > isTopSolidBlocking ( xToInt , ( int ) ( y - ( 1 - yDiff ) ) , zToInt ) )
{
this - > moveTo ( this - > x + xDiff , this - > y + yDiff , this - > z + zDiff , yRot , xRot ) ;
return ;
}
else if ( level - > isTopSolidBlocking ( xToInt , ( int ) ( y - ( 1 - yDiff ) ) - 1 , zToInt ) | | level - > getMaterial ( xToInt , ( int ) ( y - ( 1 - yDiff ) ) - 1 , zToInt ) = = Material : : water )
{
fallbackX = x + xDiff ;
fallbackY = y + yDiff ;
fallbackZ = z + zDiff ;
}
}
}
}
}
this - > moveTo ( fallbackX , fallbackY , fallbackZ , yRot , xRot ) ;
}
void Entity : : lerpTo ( double x , double y , double z , float yRot , float xRot , int steps )
{
setPos ( x , y , z ) ;
setRot ( yRot , xRot ) ;
// 4J - don't know what this special y collision is specifically for, but its definitely bad news
// for arrows as they are actually Meant to intersect the geometry they land in slightly.
if ( GetType ( ) ! = eTYPE_ARROW )
{
AABBList * collisions = level - > getCubes ( shared_from_this ( ) , bb - > shrink ( 1 / 32.0 , 0 , 1 / 32.0 ) ) ;
if ( ! collisions - > empty ( ) )
{
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 ;
}
y + = yTop - bb - > y0 ;
setPos ( x , y , z ) ;
}
}
}
float Entity : : getPickRadius ( )
{
return 0.1f ;
}
Vec3 * Entity : : getLookAngle ( )
{
return NULL ;
}
void Entity : : handleInsidePortal ( )
{
}
void Entity : : lerpMotion ( double xd , double yd , double zd )
{
this - > xd = xd ;
this - > yd = yd ;
this - > zd = zd ;
}
void Entity : : handleEntityEvent ( byte eventId )
{
}
void Entity : : animateHurt ( )
{
}
void Entity : : prepareCustomTextures ( )
{
}
ItemInstanceArray Entity : : getEquipmentSlots ( ) // ItemInstance[]
{
return ItemInstanceArray ( ) ; // Default ctor creates NULL internal array
}
// 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game
2026-03-02 15:58:20 +07:00
void Entity : : setEquippedSlot ( int slot , std : : shared_ptr < ItemInstance > item )
2026-03-01 12:16:08 +08:00
{
}
bool Entity : : isOnFire ( )
{
return onFire > 0 | | getSharedFlag ( FLAG_ONFIRE ) ;
}
bool Entity : : isRiding ( )
{
return riding ! = NULL | | getSharedFlag ( FLAG_RIDING ) ;
}
bool Entity : : isSneaking ( )
{
return getSharedFlag ( FLAG_SNEAKING ) ;
}
void Entity : : setSneaking ( bool value )
{
setSharedFlag ( FLAG_SNEAKING , value ) ;
}
bool Entity : : isIdle ( )
{
return getSharedFlag ( FLAG_IDLEANIM ) ;
}
void Entity : : setIsIdle ( bool value )
{
setSharedFlag ( FLAG_IDLEANIM , value ) ;
}
bool Entity : : isSprinting ( )
{
return getSharedFlag ( FLAG_SPRINTING ) ;
}
void Entity : : setSprinting ( bool value )
{
setSharedFlag ( FLAG_SPRINTING , value ) ;
}
bool Entity : : isInvisible ( )
{
return getSharedFlag ( FLAG_INVISIBLE ) ;
}
2026-03-02 15:58:20 +07:00
bool Entity : : isInvisibleTo ( std : : shared_ptr < Player > plr )
2026-03-01 12:16:08 +08:00
{
return isInvisible ( ) ;
}
void Entity : : setInvisible ( bool value )
{
setSharedFlag ( FLAG_INVISIBLE , value ) ;
}
bool Entity : : isWeakened ( )
{
return getSharedFlag ( FLAG_EFFECT_WEAKENED ) ;
}
void Entity : : setWeakened ( bool value )
{
setSharedFlag ( FLAG_EFFECT_WEAKENED , value ) ;
}
bool Entity : : isUsingItemFlag ( )
{
return getSharedFlag ( FLAG_USING_ITEM ) ;
}
void Entity : : setUsingItemFlag ( bool value )
{
setSharedFlag ( FLAG_USING_ITEM , value ) ;
}
bool Entity : : getSharedFlag ( int flag )
{
return ( entityData - > getByte ( DATA_SHARED_FLAGS_ID ) & ( 1 < < flag ) ) ! = 0 ;
}
void Entity : : setSharedFlag ( int flag , bool value )
{
byte currentValue = entityData - > getByte ( DATA_SHARED_FLAGS_ID ) ;
2026-03-02 15:58:20 +07:00
if ( value )
2026-03-01 12:16:08 +08:00
{
entityData - > set ( DATA_SHARED_FLAGS_ID , ( byte ) ( currentValue | ( 1 < < flag ) ) ) ;
}
else
{
entityData - > set ( DATA_SHARED_FLAGS_ID , ( byte ) ( currentValue & ~ ( 1 < < flag ) ) ) ;
}
}
// 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater.
int Entity : : getAirSupply ( )
{
return entityData - > getShort ( DATA_AIR_SUPPLY_ID ) ;
}
// 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater.
void Entity : : setAirSupply ( int supply )
{
entityData - > set ( DATA_AIR_SUPPLY_ID , ( short ) supply ) ;
}
void Entity : : thunderHit ( const LightningBolt * lightningBolt )
{
burn ( 5 ) ;
onFire + + ;
if ( onFire = = 0 ) setOnFire ( 8 ) ;
}
2026-03-02 15:58:20 +07:00
void Entity : : killed ( std : : shared_ptr < Mob > mob )
2026-03-01 12:16:08 +08:00
{
}
bool Entity : : checkInTile ( double x , double y , double z )
{
int xTile = Mth : : floor ( x ) ;
int yTile = Mth : : floor ( y ) ;
int zTile = Mth : : floor ( z ) ;
double xd = x - ( xTile ) ;
double yd = y - ( yTile ) ;
double zd = z - ( zTile ) ;
if ( level - > isSolidBlockingTile ( xTile , yTile , zTile ) )
{
bool west = ! level - > isSolidBlockingTile ( xTile - 1 , yTile , zTile ) ;
bool east = ! level - > isSolidBlockingTile ( xTile + 1 , yTile , zTile ) ;
bool up = ! level - > isSolidBlockingTile ( xTile , yTile - 1 , zTile ) ;
bool down = ! level - > isSolidBlockingTile ( xTile , yTile + 1 , zTile ) ;
bool north = ! level - > isSolidBlockingTile ( xTile , yTile , zTile - 1 ) ;
bool south = ! level - > isSolidBlockingTile ( xTile , yTile , zTile + 1 ) ;
int dir = - 1 ;
double closest = 9999 ;
if ( west & & xd < closest )
{
closest = xd ;
dir = 0 ;
}
if ( east & & 1 - xd < closest )
{
closest = 1 - xd ;
dir = 1 ;
}
if ( up & & yd < closest )
{
closest = yd ;
dir = 2 ;
}
if ( down & & 1 - yd < closest )
{
closest = 1 - yd ;
dir = 3 ;
}
if ( north & & zd < closest )
{
closest = zd ;
dir = 4 ;
}
if ( south & & 1 - zd < closest )
{
closest = 1 - zd ;
dir = 5 ;
}
float speed = random - > nextFloat ( ) * 0.2f + 0.1f ;
if ( dir = = 0 ) this - > xd = - speed ;
if ( dir = = 1 ) this - > xd = + speed ;
if ( dir = = 2 ) this - > yd = - speed ;
if ( dir = = 3 ) this - > yd = + speed ;
if ( dir = = 4 ) this - > zd = - speed ;
if ( dir = = 5 ) this - > zd = + speed ;
return true ;
}
return false ;
}
void Entity : : makeStuckInWeb ( )
{
isStuckInWeb = true ;
fallDistance = 0 ;
}
wstring Entity : : getAName ( )
{
wstring id = EntityIO : : getEncodeId ( shared_from_this ( ) ) ;
if ( id . empty ( ) ) id = L " generic " ;
return L " entity. " + id + _toString ( entityId ) ;
//return I18n.get("entity." + id + ".name");
}
2026-03-02 15:58:20 +07:00
vector < std : : shared_ptr < Entity > > * Entity : : getSubEntities ( )
2026-03-01 12:16:08 +08:00
{
return NULL ;
}
2026-03-02 15:58:20 +07:00
bool Entity : : is ( std : : shared_ptr < Entity > other )
2026-03-01 12:16:08 +08:00
{
return shared_from_this ( ) = = other ;
}
float Entity : : getYHeadRot ( )
{
return 0 ;
}
void Entity : : setYHeadRot ( float yHeadRot )
{
}
bool Entity : : isAttackable ( )
{
return true ;
}
bool Entity : : isInvulnerable ( )
{
return false ;
}
2026-03-02 15:58:20 +07:00
void Entity : : copyPosition ( std : : shared_ptr < Entity > target )
2026-03-01 12:16:08 +08:00
{
moveTo ( target - > x , target - > y , target - > z , target - > yRot , target - > xRot ) ;
}
2026-03-02 15:58:20 +07:00
void Entity : : setAnimOverrideBitmask ( unsigned int uiBitmask )
2026-03-01 12:16:08 +08:00
{
m_uiAnimOverrideBitmask = uiBitmask ;
app . DebugPrintf ( " !!! Setting anim override bitmask to %d \n " , uiBitmask ) ;
}
2026-03-02 15:58:20 +07:00
unsigned int Entity : : getAnimOverrideBitmask ( )
{
2026-03-01 12:16:08 +08:00
if ( app . GetGameSettings ( eGameSetting_CustomSkinAnim ) = = 0 )
{
// We have a force animation for some skins (claptrap)
// 4J-PB - treat all the eAnim_Disable flags as a force anim
unsigned int uiIgnoreUserCustomSkinAnimSettingMask = ( 1 < < HumanoidModel : : eAnim_ForceAnim ) |
( 1 < < HumanoidModel : : eAnim_DisableRenderArm0 ) |
( 1 < < HumanoidModel : : eAnim_DisableRenderArm1 ) |
( 1 < < HumanoidModel : : eAnim_DisableRenderTorso ) |
( 1 < < HumanoidModel : : eAnim_DisableRenderLeg0 ) |
( 1 < < HumanoidModel : : eAnim_DisableRenderLeg1 ) |
( 1 < < HumanoidModel : : eAnim_DisableRenderHair ) ;
if ( ( m_uiAnimOverrideBitmask & HumanoidModel : : m_staticBitmaskIgnorePlayerCustomAnimSetting ) ! = 0 )
{
return m_uiAnimOverrideBitmask ;
}
return 0 ;
}
return m_uiAnimOverrideBitmask ;
}