2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "net.minecraft.world.entity.h"
2026-03-03 03:04:10 +08:00
# include "net.minecraft.world.entity.animal.h"
# include "net.minecraft.world.entity.ai.attributes.h"
# include "net.minecraft.world.entity.ai.goal.h"
# include "net.minecraft.world.entity.ai.navigation.h"
# include "net.minecraft.world.entity.monster.h"
2026-03-01 12:16:08 +08:00
# include "net.minecraft.world.level.h"
# include "net.minecraft.world.level.pathfinder.h"
# include "net.minecraft.world.phys.h"
# include "SharedConstants.h"
# include "PathfinderMob.h"
2026-03-03 03:04:10 +08:00
AttributeModifier * PathfinderMob : : SPEED_MODIFIER_FLEEING = ( new AttributeModifier ( eModifierId_MOB_FLEEING , 2.0f , AttributeModifier : : OPERATION_MULTIPLY_TOTAL ) ) - > setSerialize ( false ) ;
2026-03-01 12:16:08 +08:00
PathfinderMob : : PathfinderMob ( Level * level ) : Mob ( level )
{
path = NULL ;
attackTarget = nullptr ;
holdGround = false ;
fleeTime = 0 ;
2026-03-03 03:04:10 +08:00
restrictRadius = - 1 ;
restrictCenter = new Pos ( 0 , 0 , 0 ) ;
addedLeashRestrictionGoal = false ;
leashRestrictionGoal = new MoveTowardsRestrictionGoal ( this , 1.0f ) ;
2026-03-01 12:16:08 +08:00
}
bool PathfinderMob : : shouldHoldGround ( )
{
return false ;
}
PathfinderMob : : ~ PathfinderMob ( )
{
delete path ;
2026-03-03 03:04:10 +08:00
delete restrictCenter ;
delete leashRestrictionGoal ;
2026-03-01 12:16:08 +08:00
}
void PathfinderMob : : serverAiStep ( )
{
2026-03-03 03:04:10 +08:00
if ( fleeTime > 0 )
{
if ( - - fleeTime = = 0 )
{
AttributeInstance * speed = getAttribute ( SharedMonsterAttributes : : MOVEMENT_SPEED ) ;
speed - > removeModifier ( SPEED_MODIFIER_FLEEING ) ;
}
}
2026-03-01 12:16:08 +08:00
holdGround = shouldHoldGround ( ) ;
float maxDist = 16 ;
if ( attackTarget = = NULL )
{
attackTarget = findAttackTarget ( ) ;
if ( attackTarget ! = NULL )
{
setPath ( level - > findPath ( shared_from_this ( ) , attackTarget , maxDist , true , false , false , true ) ) ; // 4J - changed to setPath from path =
}
}
else
{
if ( attackTarget - > isAlive ( ) )
{
float d = attackTarget - > distanceTo ( shared_from_this ( ) ) ;
if ( canSee ( attackTarget ) )
{
checkHurtTarget ( attackTarget , d ) ;
}
}
else
{
attackTarget = nullptr ;
}
}
/*
* if ( holdGround ) { xxa = 0 ; yya = 0 ; jumping = false ; return ; }
*/
// 4J - a few changes here so that we can call findRandomStrollLocation for a sub-set of things that it normally wouldn't be in the java game.
// This is so that we can have entities wander around a little, in order that we can measure how far they wander and then determine (if they wander too far) that
// they aren't enclosed. We don't want the extra network overhead of just having Everything wandering round all the time, so have put a management system in place
// that selects a subset of entities which have had their flag set through the considerForExtraWandering method so that these can keep doing random strolling.
if ( ! holdGround & & ( attackTarget ! = NULL & & ( path = = NULL | | random - > nextInt ( 20 ) = = 0 ) ) )
{
setPath ( level - > findPath ( shared_from_this ( ) , attackTarget , maxDist , true , false , false , true ) ) ; // 4J - changed to setPath from path =
}
else if ( ! holdGround & & ( ( path = = NULL & & ( random - > nextInt ( 180 ) = = 0 ) | | fleeTime > 0 ) | | ( random - > nextInt ( 120 ) = = 0 | | fleeTime > 0 ) ) )
{
2026-03-02 17:37:16 +07:00
if ( noActionTime < SharedConstants : : TICKS_PER_SECOND * 5 )
2026-03-01 12:16:08 +08:00
{
findRandomStrollLocation ( ) ;
}
}
else if ( ! holdGround & & ( path = = NULL ) )
{
if ( ( noActionTime > = SharedConstants : : TICKS_PER_SECOND * 5 ) & & isExtraWanderingEnabled ( ) )
{
// This entity wouldn't normally be randomly strolling. However, if our management system says that it should do, then do. Don't
// bother waiting for random conditions to be met before picking a direction though as the point here is to see if it is possible to
// stroll out of a given area and so waiting around is just wasting time
findRandomStrollLocation ( getWanderingQuadrant ( ) ) ;
}
}
// Consider this for extra strolling if it is protected against despawning. We aren't interested in ones that aren't protected as the whole point of this
// extra wandering is to potentially transition from protected to not protected.
considerForExtraWandering ( isDespawnProtected ( ) ) ;
int yFloor = Mth : : floor ( bb - > y0 + 0.5f ) ;
bool inWater = isInWater ( ) ;
bool inLava = isInLava ( ) ;
xRot = 0 ;
if ( path = = NULL | | random - > nextInt ( 100 ) = = 0 )
{
this - > Mob : : serverAiStep ( ) ;
setPath ( NULL ) ; // 4J - changed to setPath from path =
return ;
}
Vec3 * target = path - > currentPos ( shared_from_this ( ) ) ;
double r = bbWidth * 2 ;
while ( target ! = NULL & & target - > distanceToSqr ( x , target - > y , z ) < r * r )
{
path - > next ( ) ;
if ( path - > isDone ( ) )
{
target = NULL ;
setPath ( NULL ) ; // 4J - changed to setPath from path =
}
else target = path - > currentPos ( shared_from_this ( ) ) ;
}
jumping = false ;
if ( target ! = NULL )
{
double xd = target - > x - x ;
double zd = target - > z - z ;
double yd = target - > y - yFloor ;
float yRotD = ( float ) ( atan2 ( zd , xd ) * 180 / PI ) - 90 ;
float rotDiff = Mth : : wrapDegrees ( yRotD - yRot ) ;
2026-03-03 03:04:10 +08:00
yya = ( float ) getAttribute ( SharedMonsterAttributes : : MOVEMENT_SPEED ) - > getValue ( ) ;
2026-03-01 12:16:08 +08:00
if ( rotDiff > MAX_TURN )
{
rotDiff = MAX_TURN ;
}
if ( rotDiff < - MAX_TURN )
{
rotDiff = - MAX_TURN ;
}
yRot + = rotDiff ;
if ( holdGround )
{
if ( attackTarget ! = NULL )
{
double xd2 = attackTarget - > x - x ;
double zd2 = attackTarget - > z - z ;
float oldyRot = yRot ;
yRot = ( float ) ( atan2 ( zd2 , xd2 ) * 180 / PI ) - 90 ;
rotDiff = ( ( oldyRot - yRot ) + 90 ) * PI / 180 ;
xxa = - Mth : : sin ( rotDiff ) * yya * 1.0f ;
yya = Mth : : cos ( rotDiff ) * yya * 1.0f ;
}
}
if ( yd > 0 )
{
jumping = true ;
}
}
if ( attackTarget ! = NULL )
{
lookAt ( attackTarget , 30 , 30 ) ;
}
2026-03-03 03:04:10 +08:00
if ( horizontalCollision & & ! isPathFinding ( ) ) jumping = true ;
2026-03-01 12:16:08 +08:00
if ( random - > nextFloat ( ) < 0.8f & & ( inWater | | inLava ) ) jumping = true ;
}
void PathfinderMob : : findRandomStrollLocation ( int quadrant /*=-1*/ ) // 4J - added quadrant
{
bool hasBest = false ;
int xBest = - 1 ;
int yBest = - 1 ;
int zBest = - 1 ;
float best = - 99999 ;
for ( int i = 0 ; i < 10 ; i + + )
{
// 4J - added quadrant parameter to this method so that the caller can request that only stroll locations in one quadrant be found. If -1 is passed then
// behaviour is the same as the java game
int xt , zt ;
int yt = Mth : : floor ( y + random - > nextInt ( 7 ) - 3 ) ;
if ( quadrant = = - 1 )
{
xt = Mth : : floor ( x + random - > nextInt ( 13 ) - 6 ) ;
zt = Mth : : floor ( z + random - > nextInt ( 13 ) - 6 ) ;
}
else
{
int sx = ( ( quadrant & 1 ) ? - 1 : 1 ) ;
int sz = ( ( quadrant & 2 ) ? - 1 : 1 ) ;
xt = Mth : : floor ( x + random - > nextInt ( 7 ) * sx ) ;
zt = Mth : : floor ( z + random - > nextInt ( 7 ) * sz ) ;
}
float value = getWalkTargetValue ( xt , yt , zt ) ;
if ( value > best )
{
best = value ;
xBest = xt ;
yBest = yt ;
zBest = zt ;
hasBest = true ;
}
}
if ( hasBest )
{
setPath ( level - > findPath ( shared_from_this ( ) , xBest , yBest , zBest , 10 , true , false , false , true ) ) ; // 4J - changed to setPath from path =
}
}
2026-03-02 17:37:16 +07:00
void PathfinderMob : : checkHurtTarget ( shared_ptr < Entity > target , float d )
2026-03-01 12:16:08 +08:00
{
}
float PathfinderMob : : getWalkTargetValue ( int x , int y , int z )
{
return 0 ;
}
2026-03-02 17:37:16 +07:00
shared_ptr < Entity > PathfinderMob : : findAttackTarget ( )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:37:16 +07:00
return shared_ptr < Entity > ( ) ;
2026-03-01 12:16:08 +08:00
}
bool PathfinderMob : : canSpawn ( )
{
int xt = Mth : : floor ( x ) ;
int yt = Mth : : floor ( bb - > y0 ) ;
int zt = Mth : : floor ( z ) ;
return this - > Mob : : canSpawn ( ) & & getWalkTargetValue ( xt , yt , zt ) > = 0 ;
}
bool PathfinderMob : : isPathFinding ( )
{
return path ! = NULL ;
}
void PathfinderMob : : setPath ( Path * path )
{
delete this - > path ;
this - > path = path ;
}
2026-03-02 17:37:16 +07:00
shared_ptr < Entity > PathfinderMob : : getAttackTarget ( )
2026-03-01 12:16:08 +08:00
{
return attackTarget ;
}
2026-03-02 17:37:16 +07:00
void PathfinderMob : : setAttackTarget ( shared_ptr < Entity > attacker )
2026-03-01 12:16:08 +08:00
{
attackTarget = attacker ;
}
2026-03-03 03:04:10 +08:00
// might move to navigation, might make area
bool PathfinderMob : : isWithinRestriction ( )
{
return isWithinRestriction ( Mth : : floor ( x ) , Mth : : floor ( y ) , Mth : : floor ( z ) ) ;
}
bool PathfinderMob : : isWithinRestriction ( int x , int y , int z )
{
if ( restrictRadius = = - 1 ) return true ;
return restrictCenter - > distSqr ( x , y , z ) < restrictRadius * restrictRadius ;
}
void PathfinderMob : : restrictTo ( int x , int y , int z , int radius )
{
restrictCenter - > set ( x , y , z ) ;
restrictRadius = radius ;
}
Pos * PathfinderMob : : getRestrictCenter ( )
{
return restrictCenter ;
}
float PathfinderMob : : getRestrictRadius ( )
{
return restrictRadius ;
}
void PathfinderMob : : clearRestriction ( )
{
restrictRadius = - 1 ;
}
bool PathfinderMob : : hasRestriction ( )
{
return restrictRadius ! = - 1 ;
}
void PathfinderMob : : tickLeash ( )
{
Mob : : tickLeash ( ) ;
if ( isLeashed ( ) & & getLeashHolder ( ) ! = NULL & & getLeashHolder ( ) - > level = = this - > level )
{
// soft restriction
shared_ptr < Entity > leashHolder = getLeashHolder ( ) ;
restrictTo ( ( int ) leashHolder - > x , ( int ) leashHolder - > y , ( int ) leashHolder - > z , 5 ) ;
float _distanceTo = distanceTo ( leashHolder ) ;
shared_ptr < TamableAnimal > tamabaleAnimal = shared_from_this ( ) - > instanceof ( eTYPE_TAMABLE_ANIMAL ) ? dynamic_pointer_cast < TamableAnimal > ( shared_from_this ( ) ) : nullptr ;
if ( ( tamabaleAnimal ! = NULL ) & & tamabaleAnimal - > isSitting ( ) )
{
if ( _distanceTo > 10 )
{
dropLeash ( true , true ) ;
}
return ;
}
if ( ! addedLeashRestrictionGoal )
{
goalSelector . addGoal ( 2 , leashRestrictionGoal , false ) ;
getNavigation ( ) - > setAvoidWater ( false ) ;
addedLeashRestrictionGoal = true ;
}
onLeashDistance ( _distanceTo ) ;
if ( _distanceTo > 4 )
{
// harder restriction
getNavigation ( ) - > moveTo ( leashHolder , 1.0 ) ;
}
if ( _distanceTo > 6 )
{
// hardest restriction
double dx = ( leashHolder - > x - x ) / _distanceTo ;
double dy = ( leashHolder - > y - y ) / _distanceTo ;
double dz = ( leashHolder - > z - z ) / _distanceTo ;
xd + = dx * abs ( dx ) * .4 ;
yd + = dy * abs ( dy ) * .4 ;
zd + = dz * abs ( dz ) * .4 ;
}
if ( _distanceTo > 10 )
{
dropLeash ( true , true ) ;
}
}
else if ( ! isLeashed ( ) & & addedLeashRestrictionGoal )
{
addedLeashRestrictionGoal = false ;
goalSelector . removeGoal ( leashRestrictionGoal ) ;
getNavigation ( ) - > setAvoidWater ( true ) ;
clearRestriction ( ) ;
}
}
void PathfinderMob : : onLeashDistance ( float distanceToLeashHolder )
2026-03-01 12:16:08 +08:00
{
}
bool PathfinderMob : : couldWander ( )
{
return ( noActionTime < SharedConstants : : TICKS_PER_SECOND * 5 ) | | ( isExtraWanderingEnabled ( ) ) ;
}