2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "net.minecraft.world.level.h"
# include "net.minecraft.world.level.material.h"
# include "net.minecraft.world.phys.h"
# include "net.minecraft.world.level.tile.h"
# include "net.minecraft.world.damagesource.h"
# include "com.mojang.nbt.h"
# include "JavaMath.h"
# include "SharedConstants.h"
# include "ExperienceOrb.h"
# include "SoundTypes.h"
const int ExperienceOrb : : LIFETIME = 5 * 60 * SharedConstants : : TICKS_PER_SECOND ; // Five minutes!
void ExperienceOrb : : _init ( )
{
tickCount = 0 ;
age = 0 ;
throwTime = 0 ;
health = 5 ;
value = 0 ;
followingPlayer = nullptr ;
followingTime = 0 ;
}
ExperienceOrb : : ExperienceOrb ( Level * level , double x , double y , double z , int count ) : Entity ( level )
{
_init ( ) ;
setSize ( 0.5f , 0.5f ) ;
heightOffset = bbHeight / 2.0f ;
setPos ( x , y , z ) ;
yRot = ( float ) ( Math : : random ( ) * 360 ) ;
xd = ( float ) ( Math : : random ( ) * 0.2f - 0.1f ) * 2 ;
yd = ( float ) ( Math : : random ( ) * 0.2 ) * 2 ;
zd = ( float ) ( Math : : random ( ) * 0.2f - 0.1f ) * 2 ;
value = count ;
}
bool ExperienceOrb : : makeStepSound ( )
{
return false ;
}
ExperienceOrb : : ExperienceOrb ( Level * level ) : Entity ( level )
{
_init ( ) ;
setSize ( 0.25f , 0.25f ) ;
heightOffset = bbHeight / 2.0f ;
}
void ExperienceOrb : : defineSynchedData ( )
{
}
int ExperienceOrb : : getLightColor ( float a )
{
float l = 0.5f ;
if ( l < 0 ) l = 0 ;
if ( l > 1 ) l = 1 ;
int br = Entity : : getLightColor ( a ) ;
int br1 = ( br ) & 0xff ;
int br2 = ( br > > 16 ) & 0xff ;
br1 + = ( int ) ( l * 15 * 16 ) ;
if ( br1 > 15 * 16 ) br1 = 15 * 16 ;
// br2 = 15*16;
return br1 | br2 < < 16 ;
}
void ExperienceOrb : : tick ( )
{
Entity : : tick ( ) ;
if ( throwTime > 0 ) throwTime - - ;
xo = x ;
yo = y ;
zo = z ;
yd - = 0.03f ;
if ( level - > getMaterial ( Mth : : floor ( x ) , Mth : : floor ( y ) , Mth : : floor ( z ) ) = = Material : : lava )
{
yd = 0.2f ;
xd = ( random - > nextFloat ( ) - random - > nextFloat ( ) ) * 0.2f ;
zd = ( random - > nextFloat ( ) - random - > nextFloat ( ) ) * 0.2f ;
level - > playSound ( shared_from_this ( ) , eSoundType_RANDOM_FIZZ , 0.4f , 2.0f + random - > nextFloat ( ) * 0.4f ) ;
}
checkInTile ( x , ( bb - > y0 + bb - > y1 ) / 2 , z ) ;
double maxDist = 8 ;
// 4J - PC Comment
// Usually exp orbs will get created at the same time so smoothen the lagspikes
if ( followingTime < tickCount - SharedConstants : : TICKS_PER_SECOND + ( entityId % 100 ) )
{
if ( followingPlayer = = NULL | | followingPlayer - > distanceToSqr ( shared_from_this ( ) ) > maxDist * maxDist )
{
followingPlayer = level - > getNearestPlayer ( shared_from_this ( ) , maxDist ) ;
}
followingTime = tickCount ;
}
if ( followingPlayer ! = NULL )
{
double xdd = ( followingPlayer - > x - x ) / maxDist ;
double ydd = ( followingPlayer - > y + followingPlayer - > getHeadHeight ( ) - y ) / maxDist ;
double zdd = ( followingPlayer - > z - z ) / maxDist ;
double dd = sqrt ( xdd * xdd + ydd * ydd + zdd * zdd ) ;
double power = 1 - dd ;
if ( power > 0 )
{
power = power * power ;
xd + = xdd / dd * power * 0.1 ;
yd + = ydd / dd * power * 0.1 ;
zd + = zdd / dd * power * 0.1 ;
}
}
move ( xd , yd , zd ) ;
float friction = 0.98f ;
if ( onGround )
{
friction = 0.6f * 0.98f ;
int t = level - > getTile ( Mth : : floor ( x ) , Mth : : floor ( bb - > y0 ) - 1 , Mth : : floor ( z ) ) ;
if ( t > 0 )
{
friction = Tile : : tiles [ t ] - > friction * 0.98f ;
}
}
xd * = friction ;
yd * = 0.98f ;
zd * = friction ;
if ( onGround )
{
yd * = - 0.9f ;
}
tickCount + + ;
age + + ;
if ( age > = LIFETIME )
{
remove ( ) ;
}
}
bool ExperienceOrb : : updateInWaterState ( )
{
return level - > checkAndHandleWater ( bb , Material : : water , shared_from_this ( ) ) ;
}
void ExperienceOrb : : burn ( int dmg )
{
hurt ( DamageSource : : inFire , dmg ) ;
}
bool ExperienceOrb : : hurt ( DamageSource * source , int damage )
{
markHurt ( ) ;
health - = damage ;
if ( health < = 0 )
{
remove ( ) ;
}
return false ;
}
void ExperienceOrb : : addAdditonalSaveData ( CompoundTag * entityTag )
{
entityTag - > putShort ( L " Health " , ( byte ) health ) ;
entityTag - > putShort ( L " Age " , ( short ) age ) ;
entityTag - > putShort ( L " Value " , ( short ) value ) ;
}
void ExperienceOrb : : readAdditionalSaveData ( CompoundTag * tag )
{
health = tag - > getShort ( L " Health " ) & 0xff ;
age = tag - > getShort ( L " Age " ) ;
value = tag - > getShort ( L " Value " ) ;
}
2026-03-02 15:58:20 +07:00
void ExperienceOrb : : playerTouch ( std : : shared_ptr < Player > player )
2026-03-01 12:16:08 +08:00
{
if ( level - > isClientSide ) return ;
if ( throwTime = = 0 & & player - > takeXpDelay = = 0 )
{
player - > takeXpDelay = 2 ;
// 4J - sound change brought forward from 1.2.3
level - > playSound ( shared_from_this ( ) , eSoundType_RANDOM_ORB , 0.1f , 0.5f * ( ( random - > nextFloat ( ) - random - > nextFloat ( ) ) * 0.7f + 1.8f ) ) ;
player - > take ( shared_from_this ( ) , 1 ) ;
player - > increaseXp ( value ) ;
remove ( ) ;
}
}
int ExperienceOrb : : getValue ( )
{
return value ;
}
int ExperienceOrb : : getIcon ( )
{
if ( value > = 2477 )
{
return 10 ;
}
else if ( value > = 1237 )
{
return 9 ;
}
else if ( value > = 617 )
{
return 8 ;
}
else if ( value > = 307 )
{
return 7 ;
}
else if ( value > = 149 )
{
return 6 ;
}
else if ( value > = 73 )
{
return 5 ;
}
else if ( value > = 37 )
{
return 4 ;
}
else if ( value > = 17 )
{
return 3 ;
}
else if ( value > = 7 )
{
return 2 ;
}
else if ( value > = 3 )
{
return 1 ;
}
return 0 ;
}
/**
* Fetches the biggest possible experience orb value based on a maximum
* value . The current algorithm is next prime which is at least twice more
* than the previous one .
2026-03-02 15:58:20 +07:00
*
2026-03-01 12:16:08 +08:00
* @ param maxValue
* @ return
*/
int ExperienceOrb : : getExperienceValue ( int maxValue )
{
if ( maxValue > = 2477 )
{
return 2477 ;
}
else if ( maxValue > = 1237 )
{
return 1237 ;
}
else if ( maxValue > = 617 )
{
return 617 ;
}
else if ( maxValue > = 307 )
{
return 307 ;
}
else if ( maxValue > = 149 )
{
return 149 ;
}
else if ( maxValue > = 73 )
{
return 73 ;
}
else if ( maxValue > = 37 )
{
return 37 ;
}
else if ( maxValue > = 17 )
{
return 17 ;
}
else if ( maxValue > = 7 )
{
return 7 ;
}
else if ( maxValue > = 3 )
{
return 3 ;
}
return 1 ;
}
bool ExperienceOrb : : isAttackable ( )
{
return false ;
}
// 4J added
bool ExperienceOrb : : 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 ;
// 4J - don't render experience orbs that are less than 2 metres away, to try and avoid large particles that are causing us problems with photosensitivity testing - issues when you go
// near a large pile of experience orbs that all rush towards the near clip plane
if ( distance < 4 ) return false ;
return Entity : : shouldRender ( c ) ;
}