2023-06-11 07:33:35 +00:00
/ *
* This file is part of Baritone .
*
* Baritone is free software : you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* Baritone is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone . If not , see < https : //www.gnu.org/licenses/>.
* /
2023-06-17 06:55:07 +00:00
package baritone.behavior ;
2023-06-11 07:33:35 +00:00
2023-06-17 06:55:07 +00:00
import baritone.Baritone ;
2023-06-17 04:35:56 +00:00
import baritone.api.event.events.ChunkEvent ;
2023-06-11 07:33:35 +00:00
import baritone.api.event.events.TickEvent ;
2023-06-17 04:35:56 +00:00
import baritone.api.event.events.type.EventState ;
import baritone.api.utils.BetterBlockPos ;
import baritone.api.utils.Helper ;
import baritone.api.utils.Rotation ;
import baritone.api.utils.RotationUtils ;
2023-06-17 07:02:26 +00:00
import baritone.behavior.elytra.NetherPathfinderContext ;
2023-06-17 19:15:57 +00:00
import baritone.behavior.elytra.UnpackedSegment ;
2023-06-11 07:33:35 +00:00
import baritone.utils.BlockStateInterface ;
2023-06-12 05:22:01 +00:00
import com.mojang.realmsclient.util.Pair ;
2023-06-11 07:33:35 +00:00
import net.minecraft.block.material.Material ;
2023-06-13 04:43:10 +00:00
import net.minecraft.block.state.IBlockState ;
2023-06-11 07:33:35 +00:00
import net.minecraft.entity.item.EntityFireworkRocket ;
2023-06-13 05:00:23 +00:00
import net.minecraft.init.Blocks ;
import net.minecraft.util.EnumFacing ;
2023-06-11 07:33:35 +00:00
import net.minecraft.util.EnumHand ;
import net.minecraft.util.math.BlockPos ;
import net.minecraft.util.math.MathHelper ;
import net.minecraft.util.math.RayTraceResult ;
import net.minecraft.util.math.Vec3d ;
2023-06-17 03:31:53 +00:00
import net.minecraft.world.chunk.Chunk ;
2023-06-11 07:33:35 +00:00
2023-06-13 04:43:10 +00:00
import java.util.* ;
2023-06-17 19:15:57 +00:00
import java.util.concurrent.CompletableFuture ;
import java.util.function.UnaryOperator ;
2023-06-11 07:33:35 +00:00
2023-06-17 06:55:07 +00:00
public final class ElytraBehavior extends Behavior implements Helper {
2023-06-11 07:33:35 +00:00
2023-06-17 07:02:26 +00:00
/ * *
* 2b2t seed
* /
2023-06-17 03:31:53 +00:00
private static final long NETHER_SEED = 146008555100680L ;
2023-06-17 01:38:29 +00:00
// Used exclusively for PathRenderer
2023-06-17 01:53:53 +00:00
public List < Pair < Vec3d , Vec3d > > lines ;
2023-06-17 03:31:53 +00:00
public BlockPos aimPos ;
2023-06-17 01:53:53 +00:00
public List < BetterBlockPos > visiblePath ;
2023-06-17 01:38:29 +00:00
2023-06-17 06:39:21 +00:00
// :sunglasses:
2023-06-17 07:02:26 +00:00
private final NetherPathfinderContext context ;
2023-06-17 19:15:57 +00:00
private final PathManager pathManager ;
2023-06-17 01:38:29 +00:00
private int sinceFirework ;
2023-06-17 00:54:37 +00:00
2023-06-17 06:55:07 +00:00
public ElytraBehavior ( Baritone baritone ) {
2023-06-17 00:54:37 +00:00
super ( baritone ) ;
2023-06-17 07:02:26 +00:00
this . context = new NetherPathfinderContext ( NETHER_SEED ) ;
2023-06-17 01:53:53 +00:00
this . lines = new ArrayList < > ( ) ;
this . visiblePath = Collections . emptyList ( ) ;
2023-06-17 19:15:57 +00:00
this . pathManager = new PathManager ( ) ;
2023-06-17 00:54:37 +00:00
}
2023-06-17 19:15:57 +00:00
private final class PathManager {
private BlockPos destination ;
private List < BetterBlockPos > path ;
private boolean completePath ;
private int playerNear ;
private int goingTo ;
private boolean recalculating ;
public PathManager ( ) {
// lol imagine initializing fields normally
this . clear ( ) ;
2023-06-17 04:35:56 +00:00
}
2023-06-16 23:59:57 +00:00
2023-06-17 19:15:57 +00:00
public void tick ( ) {
// Recalculate closest path node
this . playerNear = this . calculateNear ( this . playerNear ) ;
2023-06-17 03:31:53 +00:00
2023-06-17 19:15:57 +00:00
// Obstacles are more important than an incomplete path, handle those first.
this . pathfindAroundObstacles ( ) ;
this . attemptNextSegment ( ) ;
2023-06-16 23:59:57 +00:00
}
2023-06-17 19:15:57 +00:00
public void pathToDestination ( BlockPos destination ) {
this . destination = destination ;
this . path0 ( ctx . playerFeet ( ) , destination , UnaryOperator . identity ( ) )
. thenRun ( ( ) - > {
final int distance = ( int ) this . pathAt ( 0 ) . distanceTo ( this . pathAt ( this . path . size ( ) - 1 ) ) ;
if ( this . completePath ) {
logDirect ( String . format ( " Computed path (%d blocks) " , distance ) ) ;
} else {
logDirect ( String . format ( " Computed segment (Next %d blocks) " , distance ) ) ;
}
} ) ;
}
2023-06-17 19:21:32 +00:00
public void pathRecalcSegment ( final int upToIncl ) {
2023-06-17 19:15:57 +00:00
if ( this . recalculating ) {
return ;
2023-06-17 04:35:56 +00:00
}
2023-06-17 19:15:57 +00:00
this . recalculating = true ;
final List < BetterBlockPos > after = this . path . subList ( upToIncl , this . path . size ( ) ) ;
final boolean complete = this . completePath ;
this . path0 ( ctx . playerFeet ( ) , this . path . get ( upToIncl ) , segment - > segment . append ( after . stream ( ) , complete ) )
. thenRun ( ( ) - > {
this . recalculating = false ;
final int recompute = this . path . size ( ) - after . size ( ) - 1 ;
final int distance = ( int ) this . pathAt ( 0 ) . distanceTo ( this . pathAt ( recompute ) ) ;
logDirect ( String . format ( " Recomputed segment (Next %d blocks) " , distance ) ) ;
} ) ;
}
2023-06-17 19:21:32 +00:00
public void pathNextSegment ( final int afterIncl ) {
2023-06-17 19:15:57 +00:00
if ( this . recalculating ) {
2023-06-17 04:35:56 +00:00
return ;
}
2023-06-17 19:15:57 +00:00
this . recalculating = true ;
final List < BetterBlockPos > before = this . path . subList ( 0 , afterIncl + 1 ) ;
this . path0 ( this . path . get ( afterIncl ) , this . destination , segment - > segment . prepend ( before . stream ( ) ) )
. thenRun ( ( ) - > {
this . recalculating = false ;
final int recompute = this . path . size ( ) - before . size ( ) - 1 ;
final int distance = ( int ) this . pathAt ( 0 ) . distanceTo ( this . pathAt ( recompute ) ) ;
if ( this . completePath ) {
logDirect ( String . format ( " Computed path (%d blocks) " , distance ) ) ;
} else {
logDirect ( String . format ( " Computed next segment (Next %d blocks) " , distance ) ) ;
}
} ) ;
}
private Vec3d pathAt ( int i ) {
return new Vec3d (
this . path . get ( i ) . x + 0 . 5 ,
this . path . get ( i ) . y + 0 . 5 ,
this . path . get ( i ) . z + 0 . 5
) ;
}
public void clear ( ) {
this . path = Collections . emptyList ( ) ;
this . playerNear = 0 ;
this . goingTo = 0 ;
this . completePath = true ;
}
private void setPath ( final UnpackedSegment segment ) {
this . path = segment . collect ( ) ;
this . removeBacktracks ( ) ;
this . playerNear = 0 ;
this . goingTo = 0 ;
this . completePath = segment . isFinished ( ) ;
}
public List < BetterBlockPos > getPath ( ) {
return this . path ;
}
public int getNear ( ) {
return this . playerNear ;
}
public void setGoingTo ( int index ) {
this . goingTo = index ;
}
public BetterBlockPos goingTo ( ) {
return this . path . get ( this . goingTo ) ;
}
// mickey resigned
private CompletableFuture < Void > path0 ( BlockPos src , BlockPos dst , UnaryOperator < UnpackedSegment > operator ) {
return ElytraBehavior . this . context . pathFindAsync ( src , dst )
. thenApply ( UnpackedSegment : : from )
. thenApply ( operator )
. thenAcceptAsync ( this : : setPath , ctx . minecraft ( ) : : addScheduledTask ) ;
}
private void pathfindAroundObstacles ( ) {
if ( this . recalculating ) {
return ;
2023-06-17 04:35:56 +00:00
}
2023-06-17 19:15:57 +00:00
outer :
while ( true ) {
int rangeStartIncl = playerNear ;
int rangeEndExcl = playerNear ;
while ( rangeEndExcl < path . size ( ) & & ctx . world ( ) . isBlockLoaded ( path . get ( rangeEndExcl ) , false ) ) {
rangeEndExcl + + ;
2023-06-17 04:35:56 +00:00
}
2023-06-17 19:15:57 +00:00
if ( rangeStartIncl > = rangeEndExcl ) {
// not loaded yet?
return ;
}
if ( ! passable ( ctx . world ( ) . getBlockState ( path . get ( rangeStartIncl ) ) ) ) {
// we're in a wall
return ; // previous iterations of this function SHOULD have fixed this by now :rage_cat:
}
for ( int i = rangeStartIncl ; i < rangeEndExcl - 1 ; i + + ) {
if ( ! clearView ( pathAt ( i ) , pathAt ( i + 1 ) ) ) {
// obstacle. where do we return to pathing?
// find the next valid segment
2023-06-17 19:21:32 +00:00
this . pathRecalcSegment ( rangeEndExcl - 1 ) ;
2023-06-17 19:15:57 +00:00
break outer ;
}
}
break ;
2023-06-17 04:35:56 +00:00
}
}
2023-06-16 23:59:57 +00:00
2023-06-17 19:15:57 +00:00
private void attemptNextSegment ( ) {
if ( this . recalculating ) {
return ;
}
2023-06-17 04:35:56 +00:00
2023-06-17 19:15:57 +00:00
final int last = this . path . size ( ) - 1 ;
if ( ! this . completePath & & ctx . world ( ) . isBlockLoaded ( this . path . get ( last ) , false ) ) {
2023-06-17 19:21:32 +00:00
this . pathNextSegment ( last ) ;
2023-06-17 19:15:57 +00:00
}
}
2023-06-17 06:39:21 +00:00
2023-06-17 19:15:57 +00:00
private int calculateNear ( int index ) {
final BetterBlockPos pos = ctx . playerFeet ( ) ;
for ( int i = index ; i > = Math . max ( index - 1000 , 0 ) ; i - = 10 ) {
if ( path . get ( i ) . distanceSq ( pos ) < path . get ( index ) . distanceSq ( pos ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
for ( int i = index ; i < Math . min ( index + 1000 , path . size ( ) ) ; i + = 10 ) {
if ( path . get ( i ) . distanceSq ( pos ) < path . get ( index ) . distanceSq ( pos ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
for ( int i = index ; i > = Math . max ( index - 50 , 0 ) ; i - - ) {
if ( path . get ( i ) . distanceSq ( pos ) < path . get ( index ) . distanceSq ( pos ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
for ( int i = index ; i < Math . min ( index + 50 , path . size ( ) ) ; i + + ) {
if ( path . get ( i ) . distanceSq ( pos ) < path . get ( index ) . distanceSq ( pos ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
return index ;
}
2023-06-17 06:39:21 +00:00
2023-06-17 19:15:57 +00:00
private void removeBacktracks ( ) {
Map < BetterBlockPos , Integer > positionFirstSeen = new HashMap < > ( ) ;
for ( int i = 0 ; i < this . path . size ( ) ; i + + ) {
BetterBlockPos pos = this . path . get ( i ) ;
if ( positionFirstSeen . containsKey ( pos ) ) {
int j = positionFirstSeen . get ( pos ) ;
while ( i > j ) {
this . path . remove ( i ) ;
i - - ;
}
2023-06-17 06:39:21 +00:00
} else {
2023-06-17 19:15:57 +00:00
positionFirstSeen . put ( pos , i ) ;
2023-06-17 06:39:21 +00:00
}
2023-06-17 19:15:57 +00:00
}
}
}
@Override
public void onChunkEvent ( ChunkEvent event ) {
if ( event . getState ( ) = = EventState . POST & & event . getType ( ) . isPopulate ( ) ) {
final Chunk chunk = ctx . world ( ) . getChunk ( event . getX ( ) , event . getZ ( ) ) ;
this . context . queueForPacking ( chunk ) ;
}
}
public void path ( BlockPos destination ) {
this . pathManager . pathToDestination ( destination ) ;
2023-06-11 07:33:35 +00:00
}
2023-06-16 03:59:01 +00:00
public void cancel ( ) {
2023-06-17 01:53:53 +00:00
this . visiblePath = Collections . emptyList ( ) ;
2023-06-17 19:15:57 +00:00
this . pathManager . clear ( ) ;
2023-06-17 03:31:53 +00:00
this . aimPos = null ;
2023-06-16 03:59:01 +00:00
this . sinceFirework = 0 ;
}
2023-06-11 07:33:35 +00:00
@Override
public void onTick ( TickEvent event ) {
if ( event . getType ( ) = = TickEvent . Type . OUT ) {
return ;
}
2023-06-17 19:15:57 +00:00
this . lines . clear ( ) ;
final List < BetterBlockPos > path = this . pathManager . getPath ( ) ;
2023-06-15 07:32:26 +00:00
if ( path . isEmpty ( ) ) {
return ;
}
2023-06-17 01:38:29 +00:00
2023-06-17 19:15:57 +00:00
this . pathManager . tick ( ) ;
final int playerNear = this . pathManager . getNear ( ) ;
this . visiblePath = path . subList (
2023-06-17 01:53:53 +00:00
Math . max ( playerNear - 30 , 0 ) ,
Math . min ( playerNear + 30 , path . size ( ) )
) ;
2023-06-17 01:38:29 +00:00
if ( ! ctx . player ( ) . isElytraFlying ( ) ) {
return ;
}
2023-06-17 20:35:26 +00:00
baritone . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2023-06-17 01:38:29 +00:00
if ( ctx . player ( ) . collidedHorizontally ) {
logDirect ( " hbonk " ) ;
}
if ( ctx . player ( ) . collidedVertically ) {
logDirect ( " vbonk " ) ;
}
Vec3d start = ctx . playerFeetAsVec ( ) ;
boolean firework = isFireworkActive ( ) ;
sinceFirework + + ;
final long t = System . currentTimeMillis ( ) ;
2023-06-17 05:39:50 +00:00
outermost :
2023-06-17 01:38:29 +00:00
for ( int relaxation = 0 ; relaxation < 3 ; relaxation + + ) { // try for a strict solution first, then relax more and more (if we're in a corner or near some blocks, it will have to relax its constraints a bit)
int [ ] heights = firework ? new int [ ] { 20 , 10 , 5 , 0 } : new int [ ] { 0 } ; // attempt to gain height, if we can, so as not to waste the boost
boolean requireClear = relaxation = = 0 ;
int steps = relaxation < 2 ? firework ? 5 : Baritone . settings ( ) . elytraSimulationTicks . value : 3 ;
int lookahead = relaxation = = 0 ? 2 : 3 ; // ideally this would be expressed as a distance in blocks, rather than a number of voxel steps
//int minStep = Math.max(0, playerNear - relaxation);
int minStep = playerNear ;
for ( int i = Math . min ( playerNear + 20 , path . size ( ) - 1 ) ; i > = minStep ; i - - ) {
for ( int dy : heights ) {
2023-06-17 19:15:57 +00:00
Vec3d dest = this . pathManager . pathAt ( i ) . add ( 0 , dy , 0 ) ;
2023-06-17 01:38:29 +00:00
if ( dy ! = 0 ) {
if ( i + lookahead > = path . size ( ) ) {
continue ;
}
if ( start . distanceTo ( dest ) < 40 ) {
2023-06-17 19:15:57 +00:00
if ( ! clearView ( dest , this . pathManager . pathAt ( i + lookahead ) . add ( 0 , dy , 0 ) ) | | ! clearView ( dest , this . pathManager . pathAt ( i + lookahead ) ) ) {
2023-06-17 01:38:29 +00:00
// aka: don't go upwards if doing so would prevent us from being able to see the next position **OR** the modified next position
2023-06-11 19:49:31 +00:00
continue ;
}
2023-06-17 01:38:29 +00:00
} else {
// but if it's far away, allow gaining altitude if we could lose it again by the time we get there
2023-06-17 19:15:57 +00:00
if ( ! clearView ( dest , this . pathManager . pathAt ( i ) ) ) {
2023-06-11 07:33:35 +00:00
continue ;
}
}
}
2023-06-17 01:38:29 +00:00
if ( requireClear ? isClear ( start , dest ) : clearView ( start , dest ) ) {
Rotation rot = RotationUtils . calcRotationFromVec3d ( start , dest , ctx . playerRotations ( ) ) ;
long a = System . currentTimeMillis ( ) ;
Float pitch = solvePitch ( dest . subtract ( start ) , steps , relaxation = = 2 ) ;
if ( pitch = = null ) {
baritone . getLookBehavior ( ) . updateTarget ( new Rotation ( rot . getYaw ( ) , ctx . playerRotations ( ) . getPitch ( ) ) , false ) ;
continue ;
}
long b = System . currentTimeMillis ( ) ;
System . out . println ( " Solved pitch in " + ( b - a ) + " total time " + ( b - t ) ) ;
2023-06-17 19:15:57 +00:00
this . pathManager . setGoingTo ( i ) ;
this . aimPos = path . get ( i ) . add ( 0 , dy , 0 ) ;
2023-06-17 01:38:29 +00:00
baritone . getLookBehavior ( ) . updateTarget ( new Rotation ( rot . getYaw ( ) , pitch ) , false ) ;
2023-06-17 05:39:50 +00:00
break outermost ;
2023-06-17 01:38:29 +00:00
}
2023-06-11 07:33:35 +00:00
}
}
2023-06-17 05:39:50 +00:00
if ( relaxation = = 2 ) {
logDirect ( " no pitch solution, probably gonna crash in a few ticks LOL!!! " ) ;
return ;
}
}
2023-06-17 19:15:57 +00:00
final BetterBlockPos goingTo = this . pathManager . goingTo ( ) ;
2023-06-17 05:39:50 +00:00
if ( ! firework
& & sinceFirework > 10
2023-06-17 19:15:57 +00:00
& & ( Baritone . settings ( ) . wasteFireworks . value | | ctx . player ( ) . posY < goingTo . y + 5 ) // don't firework if trying to descend
& & ( ctx . player ( ) . posY < goingTo . y - 5 | | start . distanceTo ( new Vec3d ( goingTo . x + 0 . 5 , ctx . player ( ) . posY , goingTo . z + 0 . 5 ) ) > 5 ) // UGH!!!!!!!
& & new Vec3d ( ctx . player ( ) . motionX , ctx . player ( ) . posY < goingTo . y ? Math . max ( 0 , ctx . player ( ) . motionY ) : ctx . player ( ) . motionY , ctx . player ( ) . motionZ ) . length ( ) < Baritone . settings ( ) . elytraFireworkSpeed . value // ignore y component if we are BOTH below where we want to be AND descending
2023-06-17 05:39:50 +00:00
) {
logDirect ( " firework " ) ;
ctx . playerController ( ) . processRightClick ( ctx . player ( ) , ctx . world ( ) , EnumHand . MAIN_HAND ) ;
sinceFirework = 0 ;
2023-06-11 07:33:35 +00:00
}
}
2023-06-17 01:38:29 +00:00
private boolean isFireworkActive ( ) {
2023-06-17 00:54:37 +00:00
// TODO: Validate that the EntityFireworkRocket is attached to ctx.player()
return ctx . world ( ) . loadedEntityList . stream ( )
. anyMatch ( x - > ( x instanceof EntityFireworkRocket ) & & ( ( EntityFireworkRocket ) x ) . isAttachedToEntity ( ) ) ;
2023-06-11 07:33:35 +00:00
}
private boolean isClear ( Vec3d start , Vec3d dest ) {
Vec3d perpendicular = dest . subtract ( start ) . crossProduct ( new Vec3d ( 0 , 1 , 0 ) ) . normalize ( ) ;
return clearView ( start , dest )
& & clearView ( start . add ( 0 , 2 , 0 ) , dest . add ( 0 , 2 , 0 ) )
& & clearView ( start . add ( 0 , - 2 , 0 ) , dest . add ( 0 , - 2 , 0 ) )
& & clearView ( start . add ( perpendicular ) , dest . add ( perpendicular ) )
& & clearView ( start . subtract ( perpendicular ) , dest . subtract ( perpendicular ) ) ;
}
private boolean clearView ( Vec3d start , Vec3d dest ) {
2023-06-12 05:22:01 +00:00
lines . add ( Pair . of ( start , dest ) ) ;
2023-06-13 05:00:23 +00:00
RayTraceResult result = rayTraceBlocks ( start , dest ) ;
2023-06-11 07:33:35 +00:00
return result = = null | | result . typeOfHit = = RayTraceResult . Type . MISS ;
}
2023-06-11 20:57:21 +00:00
private Float solvePitch ( Vec3d goalDirection , int steps , boolean desperate ) {
2023-06-11 07:33:35 +00:00
// we are at a certain velocity, but we have a target velocity
// what pitch would get us closest to our target velocity?
// yaw is easy so we only care about pitch
goalDirection = goalDirection . normalize ( ) ;
Rotation good = RotationUtils . calcRotationFromVec3d ( new Vec3d ( 0 , 0 , 0 ) , goalDirection , ctx . playerRotations ( ) ) ; // lazy lol
2023-06-17 01:38:29 +00:00
boolean firework = isFireworkActive ( ) ;
2023-06-11 07:33:35 +00:00
Float bestPitch = null ;
double bestDot = Double . NEGATIVE_INFINITY ;
Vec3d motion = new Vec3d ( ctx . player ( ) . motionX , ctx . player ( ) . motionY , ctx . player ( ) . motionZ ) ;
BlockStateInterface bsi = new BlockStateInterface ( ctx ) ;
2023-06-11 20:57:21 +00:00
float minPitch = desperate ? - 90 : Math . max ( good . getPitch ( ) - Baritone . settings ( ) . elytraPitchRange . value , - 89 ) ;
float maxPitch = desperate ? 90 : Math . min ( good . getPitch ( ) + Baritone . settings ( ) . elytraPitchRange . value , 89 ) ;
2023-06-11 07:33:35 +00:00
outer :
2023-06-11 20:57:21 +00:00
for ( float pitch = minPitch ; pitch < = maxPitch ; pitch + + ) {
2023-06-11 07:33:35 +00:00
Vec3d stepped = motion ;
Vec3d totalMotion = new Vec3d ( 0 , 0 , 0 ) ;
for ( int i = 0 ; i < steps ; i + + ) {
stepped = step ( stepped , pitch , good . getYaw ( ) , firework ) ;
2023-06-11 19:49:31 +00:00
Vec3d actualPositionPrevTick = ctx . playerFeetAsVec ( ) . add ( totalMotion ) ;
2023-06-11 07:33:35 +00:00
totalMotion = totalMotion . add ( stepped ) ;
Vec3d actualPosition = ctx . playerFeetAsVec ( ) . add ( totalMotion ) ;
2023-06-11 19:49:31 +00:00
for ( int x = MathHelper . floor ( Math . min ( actualPosition . x , actualPositionPrevTick . x ) - 0 . 31 ) ; x < = Math . max ( actualPosition . x , actualPositionPrevTick . x ) + 0 . 31 ; x + + ) {
for ( int y = MathHelper . floor ( Math . min ( actualPosition . y , actualPositionPrevTick . y ) - 0 . 2 ) ; y < = Math . max ( actualPosition . y , actualPositionPrevTick . y ) + 0 . 8 ; y + + ) {
for ( int z = MathHelper . floor ( Math . min ( actualPosition . z , actualPositionPrevTick . z ) - 0 . 31 ) ; z < = Math . max ( actualPosition . z , actualPositionPrevTick . z ) + 0 . 31 ; z + + ) {
2023-06-13 04:43:10 +00:00
if ( ! passable ( bsi . get0 ( x , y , z ) ) ) {
2023-06-11 19:49:31 +00:00
continue outer ;
}
}
}
2023-06-11 07:33:35 +00:00
}
}
double directionalGoodness = goalDirection . dotProduct ( totalMotion . normalize ( ) ) ;
// tried to incorporate a "speedGoodness" but it kept making it do stupid stuff (aka always losing altitude)
double goodness = directionalGoodness ;
if ( goodness > bestDot ) {
bestDot = goodness ;
bestPitch = pitch ;
}
}
return bestPitch ;
}
2023-06-17 07:02:26 +00:00
public static boolean passable ( IBlockState state ) {
2023-06-13 04:43:10 +00:00
return state . getMaterial ( ) = = Material . AIR ;
}
2023-06-17 00:54:37 +00:00
private static Vec3d step ( Vec3d motion , float rotationPitch , float rotationYaw , boolean firework ) {
2023-06-11 07:33:35 +00:00
double motionX = motion . x ;
double motionY = motion . y ;
double motionZ = motion . z ;
float flatZ = MathHelper . cos ( - rotationYaw * 0 . 017453292F - ( float ) Math . PI ) ; // 0.174... is Math.PI / 180
float flatX = MathHelper . sin ( - rotationYaw * 0 . 017453292F - ( float ) Math . PI ) ;
float pitchBase = - MathHelper . cos ( - rotationPitch * 0 . 017453292F ) ;
float pitchHeight = MathHelper . sin ( - rotationPitch * 0 . 017453292F ) ;
Vec3d lookDirection = new Vec3d ( flatX * pitchBase , pitchHeight , flatZ * pitchBase ) ;
2023-06-17 01:38:29 +00:00
2023-06-11 19:49:31 +00:00
if ( firework ) {
2023-06-17 01:38:29 +00:00
// See EntityFireworkRocket
2023-06-11 19:49:31 +00:00
motionX + = lookDirection . x * 0 . 1 + ( lookDirection . x * 1 . 5 - motionX ) * 0 . 5 ;
motionY + = lookDirection . y * 0 . 1 + ( lookDirection . y * 1 . 5 - motionY ) * 0 . 5 ;
motionZ + = lookDirection . z * 0 . 1 + ( lookDirection . z * 1 . 5 - motionZ ) * 0 . 5 ;
}
2023-06-17 01:38:29 +00:00
2023-06-11 07:33:35 +00:00
float pitchRadians = rotationPitch * 0 . 017453292F ;
double pitchBase2 = Math . sqrt ( lookDirection . x * lookDirection . x + lookDirection . z * lookDirection . z ) ;
double flatMotion = Math . sqrt ( motionX * motionX + motionZ * motionZ ) ;
double thisIsAlwaysOne = lookDirection . length ( ) ;
float pitchBase3 = MathHelper . cos ( pitchRadians ) ;
//System.out.println("always the same lol " + -pitchBase + " " + pitchBase3);
//System.out.println("always the same lol " + Math.abs(pitchBase3) + " " + pitchBase2);
//System.out.println("always 1 lol " + thisIsAlwaysOne);
pitchBase3 = ( float ) ( ( double ) pitchBase3 * ( double ) pitchBase3 * Math . min ( 1 , thisIsAlwaysOne / 0 . 4 ) ) ;
motionY + = - 0 . 08 + ( double ) pitchBase3 * 0 . 06 ;
if ( motionY < 0 & & pitchBase2 > 0 ) {
double speedModifier = motionY * - 0 . 1 * ( double ) pitchBase3 ;
motionY + = speedModifier ;
motionX + = lookDirection . x * speedModifier / pitchBase2 ;
motionZ + = lookDirection . z * speedModifier / pitchBase2 ;
}
if ( pitchRadians < 0 ) { // if you are looking down (below level)
double anotherSpeedModifier = flatMotion * ( double ) ( - MathHelper . sin ( pitchRadians ) ) * 0 . 04 ;
motionY + = anotherSpeedModifier * 3 . 2 ;
motionX - = lookDirection . x * anotherSpeedModifier / pitchBase2 ;
motionZ - = lookDirection . z * anotherSpeedModifier / pitchBase2 ;
}
if ( pitchBase2 > 0 ) { // this is always true unless you are looking literally straight up (let's just say the bot will never do that)
motionX + = ( lookDirection . x / pitchBase2 * flatMotion - motionX ) * 0 . 1 ;
motionZ + = ( lookDirection . z / pitchBase2 * flatMotion - motionZ ) * 0 . 1 ;
}
motionX * = 0 . 99 ;
motionY * = 0 . 98 ;
motionZ * = 0 . 99 ;
//System.out.println(motionX + " " + motionY + " " + motionZ);
2023-06-11 19:49:31 +00:00
2023-06-11 07:33:35 +00:00
return new Vec3d ( motionX , motionY , motionZ ) ;
}
2023-06-17 00:54:37 +00:00
// TODO: Use the optimized version from builder-2
private RayTraceResult rayTraceBlocks ( Vec3d start , Vec3d end ) {
2023-06-13 05:00:23 +00:00
int x1 = MathHelper . floor ( end . x ) ;
int y1 = MathHelper . floor ( end . y ) ;
int z1 = MathHelper . floor ( end . z ) ;
int x2 = MathHelper . floor ( start . x ) ;
int y2 = MathHelper . floor ( start . y ) ;
int z2 = MathHelper . floor ( start . z ) ;
BlockPos blockpos = new BlockPos ( x2 , y2 , z2 ) ;
IBlockState iblockstate = ctx . world ( ) . getBlockState ( blockpos ) ;
if ( ! passable ( iblockstate ) ) {
2023-06-17 04:35:56 +00:00
return Blocks . SPONGE . getDefaultState ( ) . collisionRayTrace ( ctx . world ( ) , blockpos , start , end ) ;
2023-06-13 05:00:23 +00:00
}
int steps = 200 ;
while ( steps - - > = 0 ) {
if ( Double . isNaN ( start . x ) | | Double . isNaN ( start . y ) | | Double . isNaN ( start . z ) ) {
return null ;
}
if ( x2 = = x1 & & y2 = = y1 & & z2 = = z1 ) {
return null ;
}
boolean hitX = true ;
boolean hitY = true ;
boolean hitZ = true ;
double nextX = 999 . 0D ;
double nextY = 999 . 0D ;
double nextZ = 999 . 0D ;
if ( x1 > x2 ) {
nextX = ( double ) x2 + 1 . 0D ;
} else if ( x1 < x2 ) {
nextX = ( double ) x2 + 0 . 0D ;
} else {
hitX = false ;
}
if ( y1 > y2 ) {
nextY = ( double ) y2 + 1 . 0D ;
} else if ( y1 < y2 ) {
nextY = ( double ) y2 + 0 . 0D ;
} else {
hitY = false ;
}
if ( z1 > z2 ) {
nextZ = ( double ) z2 + 1 . 0D ;
} else if ( z1 < z2 ) {
nextZ = ( double ) z2 + 0 . 0D ;
} else {
hitZ = false ;
}
double stepX = 999 . 0D ;
double stepY = 999 . 0D ;
double stepZ = 999 . 0D ;
double dirX = end . x - start . x ;
double dirY = end . y - start . y ;
double dirZ = end . z - start . z ;
if ( hitX ) {
stepX = ( nextX - start . x ) / dirX ;
}
if ( hitY ) {
stepY = ( nextY - start . y ) / dirY ;
}
if ( hitZ ) {
stepZ = ( nextZ - start . z ) / dirZ ;
}
if ( stepX = = - 0 . 0D ) {
stepX = - 1 . 0E - 4D ;
}
if ( stepY = = - 0 . 0D ) {
stepY = - 1 . 0E - 4D ;
}
if ( stepZ = = - 0 . 0D ) {
stepZ = - 1 . 0E - 4D ;
}
EnumFacing dir ;
if ( stepX < stepY & & stepX < stepZ ) {
dir = x1 > x2 ? EnumFacing . WEST : EnumFacing . EAST ;
start = new Vec3d ( nextX , start . y + dirY * stepX , start . z + dirZ * stepX ) ;
} else if ( stepY < stepZ ) {
dir = y1 > y2 ? EnumFacing . DOWN : EnumFacing . UP ;
start = new Vec3d ( start . x + dirX * stepY , nextY , start . z + dirZ * stepY ) ;
} else {
dir = z1 > z2 ? EnumFacing . NORTH : EnumFacing . SOUTH ;
start = new Vec3d ( start . x + dirX * stepZ , start . y + dirY * stepZ , nextZ ) ;
}
x2 = MathHelper . floor ( start . x ) - ( dir = = EnumFacing . EAST ? 1 : 0 ) ;
y2 = MathHelper . floor ( start . y ) - ( dir = = EnumFacing . UP ? 1 : 0 ) ;
z2 = MathHelper . floor ( start . z ) - ( dir = = EnumFacing . SOUTH ? 1 : 0 ) ;
blockpos = new BlockPos ( x2 , y2 , z2 ) ;
IBlockState iblockstate1 = ctx . world ( ) . getBlockState ( blockpos ) ;
if ( ! passable ( iblockstate1 ) ) {
2023-06-17 04:35:56 +00:00
return Blocks . NETHERRACK . getDefaultState ( ) . collisionRayTrace ( ctx . world ( ) , blockpos , start , end ) ;
2023-06-13 05:00:23 +00:00
}
}
return null ;
}
2023-06-11 07:33:35 +00:00
}