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-18 05:24:10 +00:00
import baritone.api.behavior.IElytraBehavior ;
2023-06-25 04:44:08 +00:00
import baritone.api.behavior.look.IAimProcessor ;
import baritone.api.behavior.look.ITickableAimProcessor ;
2023-06-21 01:09:53 +00:00
import baritone.api.event.events.* ;
2023-06-18 15:57:13 +00:00
import baritone.api.utils.* ;
2023-06-17 07:02:26 +00:00
import baritone.behavior.elytra.NetherPathfinderContext ;
2023-06-21 02:07:21 +00:00
import baritone.behavior.elytra.NetherPath ;
2023-06-21 05:52:43 +00:00
import baritone.behavior.elytra.PathCalculationException ;
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-18 05:24:10 +00:00
import baritone.utils.accessor.IEntityFireworkRocket ;
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-18 05:24:10 +00:00
import net.minecraft.init.Items ;
import net.minecraft.item.ItemStack ;
import net.minecraft.nbt.NBTTagCompound ;
2023-06-20 02:22:57 +00:00
import net.minecraft.network.play.server.SPacketPlayerPosLook ;
2023-06-11 07:33:35 +00:00
import net.minecraft.util.EnumHand ;
2023-06-19 22:26:00 +00:00
import net.minecraft.util.math.* ;
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 ;
2023-06-21 03:36:27 +00:00
import java.util.concurrent.CopyOnWriteArrayList ;
2023-06-21 01:09:53 +00:00
import java.util.concurrent.Future ;
2023-06-17 19:15:57 +00:00
import java.util.function.UnaryOperator ;
2023-06-11 07:33:35 +00:00
2023-06-18 05:24:10 +00:00
public final class ElytraBehavior extends Behavior implements IElytraBehavior , 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-19 01:06:32 +00:00
public List < Pair < Vec3d , Vec3d > > clearLines ;
public List < Pair < Vec3d , Vec3d > > blockedLines ;
2023-06-25 05:27:38 +00:00
public List < Vec3d > simulationLine ;
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-23 23:30:10 +00:00
/ * *
* Remaining cool - down ticks between firework usage
* /
2023-06-20 04:12:46 +00:00
private int remainingFireworkTicks ;
2023-06-23 23:30:10 +00:00
2023-06-26 20:35:40 +00:00
/ * *
* Remaining cool - down ticks after the player ' s position and rotation are reset by the server
* /
private int remainingSetBackTicks ;
2023-06-23 23:30:10 +00:00
/ * *
* The most recent minimum number of firework boost ticks , equivalent to { @code 10 * ( 1 + Flight ) }
* < p >
* Updated every time a firework is automatically used
* /
private int minimumBoostTicks ;
2023-06-19 20:29:30 +00:00
private BlockStateInterface bsi ;
2023-06-17 00:54:37 +00:00
2023-06-21 01:09:53 +00:00
private Future < Solution > solver ;
private boolean solveNextTick ;
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-21 03:36:27 +00:00
this . clearLines = new CopyOnWriteArrayList < > ( ) ;
this . blockedLines = new CopyOnWriteArrayList < > ( ) ;
2023-06-17 01:53:53 +00:00
this . visiblePath = Collections . emptyList ( ) ;
2023-06-21 01:09:53 +00:00
this . pathManager = this . 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 ;
2023-06-21 02:07:21 +00:00
private NetherPath path ;
2023-06-17 19:15:57 +00:00
private boolean completePath ;
private boolean recalculating ;
2023-06-20 18:43:14 +00:00
private int maxPlayerNear ;
private int ticksNearUnchanged ;
2023-06-19 20:16:11 +00:00
private int playerNear ;
2023-06-17 19:15:57 +00:00
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
2023-06-21 01:09:53 +00:00
this . updatePlayerNear ( ) ;
2023-06-20 18:43:14 +00:00
final int prevMaxNear = this . maxPlayerNear ;
this . maxPlayerNear = Math . max ( this . maxPlayerNear , this . playerNear ) ;
2023-06-21 05:21:37 +00:00
if ( this . maxPlayerNear = = prevMaxNear & & ctx . player ( ) . isElytraFlying ( ) ) {
2023-06-20 18:43:14 +00:00
this . ticksNearUnchanged + + ;
} else {
this . ticksNearUnchanged = 0 ;
}
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
2023-06-21 05:52:43 +00:00
public void pathToDestination ( final BlockPos destination ) {
2023-06-17 19:15:57 +00:00
this . destination = destination ;
2023-06-19 06:09:43 +00:00
final long start = System . nanoTime ( ) ;
2023-06-17 19:15:57 +00:00
this . path0 ( ctx . playerFeet ( ) , destination , UnaryOperator . identity ( ) )
2023-06-18 01:16:26 +00:00
. thenRun ( ( ) - > {
2023-06-21 01:18:56 +00:00
final double distance = this . path . get ( 0 ) . distanceTo ( this . path . get ( this . path . size ( ) - 1 ) ) ;
2023-06-17 19:15:57 +00:00
if ( this . completePath ) {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed path (%.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
} else {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed segment (Next %.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
}
2023-06-18 01:16:26 +00:00
} )
. whenComplete ( ( result , ex ) - > {
this . recalculating = false ;
2023-06-22 23:07:11 +00:00
if ( ex ! = null ) {
final Throwable cause = ex . getCause ( ) ;
if ( cause instanceof PathCalculationException ) {
logDirect ( " Failed to compute path to destination " ) ;
} else {
logUnhandledException ( cause ) ;
}
2023-06-18 01:16:26 +00:00
}
2023-06-17 19:15:57 +00:00
} ) ;
}
2023-06-20 18:43:14 +00:00
public CompletableFuture < Void > pathRecalcSegment ( final int upToIncl ) {
2023-06-17 19:15:57 +00:00
if ( this . recalculating ) {
2023-06-20 18:43:14 +00:00
throw new IllegalStateException ( " already recalculating " ) ;
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 ;
2023-06-20 18:43:14 +00:00
return this . path0 ( ctx . playerFeet ( ) , this . path . get ( upToIncl ) , segment - > segment . append ( after . stream ( ) , complete ) )
2023-06-18 01:16:26 +00:00
. whenComplete ( ( result , ex ) - > {
2023-06-17 19:15:57 +00:00
this . recalculating = false ;
2023-06-22 23:07:11 +00:00
if ( ex ! = null ) {
final Throwable cause = ex . getCause ( ) ;
if ( cause instanceof PathCalculationException ) {
logDirect ( " Failed to recompute segment " ) ;
} else {
logUnhandledException ( cause ) ;
}
2023-06-18 00:58:19 +00:00
}
2023-06-17 19:15:57 +00:00
} ) ;
}
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 ) ;
2023-06-19 06:09:43 +00:00
final long start = System . nanoTime ( ) ;
2023-06-17 19:15:57 +00:00
this . path0 ( this . path . get ( afterIncl ) , this . destination , segment - > segment . prepend ( before . stream ( ) ) )
2023-06-18 01:16:26 +00:00
. thenRun ( ( ) - > {
2023-06-17 19:15:57 +00:00
final int recompute = this . path . size ( ) - before . size ( ) - 1 ;
2023-06-21 01:18:56 +00:00
final double distance = this . path . get ( 0 ) . distanceTo ( this . path . get ( recompute ) ) ;
2023-06-17 19:15:57 +00:00
if ( this . completePath ) {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed path (%.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
} else {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed segment (Next %.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
}
2023-06-18 01:16:26 +00:00
} )
. whenComplete ( ( result , ex ) - > {
this . recalculating = false ;
2023-06-22 23:07:11 +00:00
if ( ex ! = null ) {
final Throwable cause = ex . getCause ( ) ;
if ( cause instanceof PathCalculationException ) {
logDirect ( " Failed to compute next segment " ) ;
} else {
logUnhandledException ( cause ) ;
}
2023-06-18 01:16:26 +00:00
}
2023-06-17 19:15:57 +00:00
} ) ;
}
public void clear ( ) {
2023-06-19 20:16:11 +00:00
this . destination = null ;
2023-06-21 02:07:21 +00:00
this . path = NetherPath . emptyPath ( ) ;
2023-06-17 19:15:57 +00:00
this . completePath = true ;
2023-06-19 20:15:29 +00:00
this . recalculating = false ;
2023-06-19 20:16:11 +00:00
this . playerNear = 0 ;
2023-06-20 18:43:14 +00:00
this . ticksNearUnchanged = 0 ;
this . maxPlayerNear = 0 ;
2023-06-17 19:15:57 +00:00
}
private void setPath ( final UnpackedSegment segment ) {
this . path = segment . collect ( ) ;
this . completePath = segment . isFinished ( ) ;
2023-06-20 18:43:14 +00:00
this . playerNear = 0 ;
this . ticksNearUnchanged = 0 ;
this . maxPlayerNear = 0 ;
2023-06-17 19:15:57 +00:00
}
2023-06-21 02:07:21 +00:00
public NetherPath getPath ( ) {
2023-06-17 19:15:57 +00:00
return this . path ;
}
public int getNear ( ) {
return this . playerNear ;
}
// 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
2023-06-19 20:29:30 +00:00
int rangeStartIncl = playerNear ;
int rangeEndExcl = playerNear ;
while ( rangeEndExcl < path . size ( ) & & ctx . world ( ) . isBlockLoaded ( path . get ( rangeEndExcl ) , false ) ) {
rangeEndExcl + + ;
}
if ( rangeStartIncl > = rangeEndExcl ) {
// not loaded yet?
return ;
}
2023-06-19 21:58:06 +00:00
if ( ! passable ( ctx . world ( ) . getBlockState ( path . get ( rangeStartIncl ) ) , false ) ) {
2023-06-19 20:29:30 +00:00
// we're in a wall
return ; // previous iterations of this function SHOULD have fixed this by now :rage_cat:
}
2023-06-20 18:43:14 +00:00
if ( this . ticksNearUnchanged > 100 ) {
this . pathRecalcSegment ( rangeEndExcl - 1 )
. thenRun ( ( ) - > {
logDirect ( " Recalculating segment, no progress in last 100 ticks " ) ;
} ) ;
this . ticksNearUnchanged = 0 ;
return ;
}
2023-06-19 20:29:30 +00:00
for ( int i = rangeStartIncl ; i < rangeEndExcl - 1 ; i + + ) {
2023-06-21 02:07:21 +00:00
if ( ! ElytraBehavior . this . clearView ( this . path . getVec ( i ) , this . path . getVec ( i + 1 ) , false ) ) {
2023-06-19 20:29:30 +00:00
// obstacle. where do we return to pathing?
// find the next valid segment
2023-06-20 18:43:14 +00:00
final BetterBlockPos blockage = this . path . get ( i ) ;
2023-06-21 01:09:53 +00:00
final double distance = ctx . playerFeet ( ) . distanceTo ( this . path . get ( rangeEndExcl - 1 ) ) ;
2023-06-20 18:43:14 +00:00
final long start = System . nanoTime ( ) ;
this . pathRecalcSegment ( rangeEndExcl - 1 )
. thenRun ( ( ) - > {
logDirect ( String . format ( " Recalculated segment around path blockage near %s %s %s (next %.1f blocks in %.4f seconds) " ,
SettingsUtil . maybeCensor ( blockage . x ) ,
SettingsUtil . maybeCensor ( blockage . y ) ,
SettingsUtil . maybeCensor ( blockage . z ) ,
distance ,
( System . nanoTime ( ) - start ) / 1e9d
) ) ;
} ) ;
2023-06-17 19:15:57 +00:00
return ;
}
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-21 01:09:53 +00:00
public void updatePlayerNear ( ) {
2023-06-22 02:19:12 +00:00
if ( this . path . isEmpty ( ) ) {
return ;
}
2023-06-21 01:09:53 +00:00
int index = this . playerNear ;
2023-06-17 19:15:57 +00:00
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
}
}
2023-06-21 01:09:53 +00:00
this . playerNear = index ;
2023-06-17 19:15:57 +00:00
}
}
@Override
public void onChunkEvent ( ChunkEvent event ) {
2023-06-19 02:25:00 +00:00
if ( event . isPostPopulate ( ) ) {
2023-06-17 19:15:57 +00:00
final Chunk chunk = ctx . world ( ) . getChunk ( event . getX ( ) , event . getZ ( ) ) ;
this . context . queueForPacking ( chunk ) ;
}
}
2023-06-19 02:25:00 +00:00
@Override
public void onBlockChange ( BlockChangeEvent event ) {
event . getAffectedChunks ( ) . stream ( )
. map ( pos - > ctx . world ( ) . getChunk ( pos . x , pos . z ) )
. forEach ( this . context : : queueForPacking ) ;
}
2023-06-20 02:22:57 +00:00
@Override
public void onReceivePacket ( PacketEvent event ) {
if ( event . getPacket ( ) instanceof SPacketPlayerPosLook ) {
2023-06-20 04:12:46 +00:00
ctx . minecraft ( ) . addScheduledTask ( ( ) - > {
this . remainingSetBackTicks = Baritone . settings ( ) . elytraFireworkSetbackUseDelay . value ;
} ) ;
2023-06-20 02:22:57 +00:00
}
}
2023-06-19 02:46:03 +00:00
@Override
public void pathTo ( BlockPos destination ) {
2023-06-17 19:15:57 +00:00
this . pathManager . pathToDestination ( destination ) ;
2023-06-11 07:33:35 +00:00
}
2023-06-19 02:46:03 +00:00
@Override
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-20 04:12:46 +00:00
this . remainingFireworkTicks = 0 ;
this . remainingSetBackTicks = 0 ;
2023-06-21 04:15:45 +00:00
if ( this . solver ! = null ) {
this . solver . cancel ( true ) ;
this . solver = null ;
}
2023-06-18 05:24:10 +00:00
}
2023-06-19 02:46:03 +00:00
@Override
2023-06-18 05:24:10 +00:00
public boolean isActive ( ) {
2023-06-19 02:46:03 +00:00
return ! this . pathManager . getPath ( ) . isEmpty ( ) ;
2023-06-16 03:59:01 +00:00
}
2023-06-11 07:33:35 +00:00
@Override
public void onTick ( TickEvent event ) {
if ( event . getType ( ) = = TickEvent . Type . OUT ) {
return ;
}
2023-06-19 02:46:03 +00:00
2023-06-21 01:09:53 +00:00
// Fetch the previous solution, regardless of if it's going to be used
Solution solution = null ;
if ( this . solver ! = null ) {
try {
solution = this . solver . get ( ) ;
} catch ( Exception ignored ) {
// it doesn't matter if get() fails since the solution can just be recalculated synchronously
} finally {
this . solver = null ;
}
}
2023-06-20 04:12:46 +00:00
// Certified mojang employee incident
if ( this . remainingFireworkTicks > 0 ) {
this . remainingFireworkTicks - - ;
}
if ( this . remainingSetBackTicks > 0 ) {
this . remainingSetBackTicks - - ;
}
2023-06-26 19:00:38 +00:00
if ( ! this . getAttachedFirework ( ) . isPresent ( ) ) {
this . minimumBoostTicks = 0 ;
}
2023-06-20 04:12:46 +00:00
2023-06-21 02:07:21 +00:00
// Reset rendered elements
2023-06-19 01:06:32 +00:00
this . clearLines . clear ( ) ;
this . blockedLines . clear ( ) ;
2023-06-25 05:27:38 +00:00
this . simulationLine = null ;
2023-06-21 02:07:21 +00:00
this . aimPos = null ;
2023-06-17 19:15:57 +00:00
final List < BetterBlockPos > path = this . pathManager . getPath ( ) ;
2023-06-21 05:21:37 +00:00
if ( path . isEmpty ( ) ) {
2023-06-15 07:32:26 +00:00
return ;
}
2023-06-17 01:38:29 +00:00
2023-06-19 00:57:29 +00:00
this . bsi = new BlockStateInterface ( ctx ) ;
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 ) ,
2023-06-18 03:03:30 +00:00
Math . min ( playerNear + 100 , path . size ( ) )
2023-06-17 01:53:53 +00:00
) ;
2023-06-21 05:21:37 +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 " ) ;
}
2023-06-25 04:44:08 +00:00
final SolverContext solverContext = this . new SolverContext ( false ) ;
2023-06-21 01:09:53 +00:00
this . solveNextTick = true ;
// If there's no previously calculated solution to use, or the context used at the end of last tick doesn't match this tick
if ( solution = = null | | ! solution . context . equals ( solverContext ) ) {
solution = this . solveAngles ( solverContext ) ;
}
2023-06-20 20:15:02 +00:00
if ( solution = = null ) {
logDirect ( " no solution " ) ;
return ;
}
baritone . getLookBehavior ( ) . updateTarget ( solution . rotation , false ) ;
if ( ! solution . solvedPitch ) {
logDirect ( " no pitch solution, probably gonna crash in a few ticks LOL!!! " ) ;
return ;
2023-06-21 01:09:53 +00:00
} else {
2023-06-21 01:30:10 +00:00
this . aimPos = new BetterBlockPos ( solution . goingTo . x , solution . goingTo . y , solution . goingTo . z ) ;
2023-06-20 20:15:02 +00:00
}
2023-06-21 01:09:53 +00:00
this . tickUseFireworks (
solution . context . start ,
solution . goingTo ,
2023-06-26 18:55:18 +00:00
solution . context . boost . isBoosted ( ) ,
2023-06-21 01:09:53 +00:00
solution . forceUseFirework
) ;
2023-06-20 20:15:02 +00:00
}
2023-06-21 01:09:53 +00:00
@Override
public void onPostTick ( TickEvent event ) {
if ( event . getType ( ) = = TickEvent . Type . IN & & this . solveNextTick ) {
// We're at the end of the tick, the player's position likely updated and the closest path node could've
// changed. Updating it now will avoid unnecessary recalculation on the main thread.
this . pathManager . updatePlayerNear ( ) ;
2023-06-25 04:44:08 +00:00
final SolverContext context = this . new SolverContext ( true ) ;
2023-06-21 01:09:53 +00:00
this . solver = CompletableFuture . supplyAsync ( ( ) - > this . solveAngles ( context ) ) ;
this . solveNextTick = false ;
}
}
private Solution solveAngles ( final SolverContext context ) {
2023-06-21 02:07:21 +00:00
final NetherPath path = context . path ;
2023-06-21 01:09:53 +00:00
final int playerNear = context . playerNear ;
final Vec3d start = context . start ;
2023-06-19 21:58:06 +00:00
final boolean isInLava = ctx . player ( ) . isInLava ( ) ;
2023-06-20 20:05:59 +00:00
Solution solution = null ;
2023-06-18 05:11:40 +00:00
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)
2023-06-26 18:55:18 +00:00
int [ ] heights = context . boost . isBoosted ( ) ? new int [ ] { 20 , 10 , 5 , 0 } : new int [ ] { 0 } ; // attempt to gain height, if we can, so as not to waste the boost
2023-06-21 02:28:30 +00:00
float [ ] interps = new float [ ] { 1 . 0f , 0 . 75f , 0 . 5f , 0 . 25f } ;
2023-06-17 01:38:29 +00:00
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-20 02:36:09 +00:00
for ( float interp : interps ) {
Vec3d dest ;
if ( interp = = 1 | | i = = minStep ) {
2023-06-21 02:07:21 +00:00
dest = path . getVec ( i ) ;
2023-06-20 02:36:09 +00:00
} else {
2023-06-21 02:07:21 +00:00
dest = path . getVec ( i ) . scale ( interp ) . add ( path . getVec ( i - 1 ) . scale ( 1 . 0d - interp ) ) ;
2023-06-17 01:38:29 +00:00
}
2023-06-20 02:36:09 +00:00
dest = dest . add ( 0 , dy , 0 ) ;
if ( dy ! = 0 ) {
if ( i + lookahead > = path . size ( ) ) {
2023-06-11 19:49:31 +00:00
continue ;
}
2023-06-20 02:36:09 +00:00
if ( start . distanceTo ( dest ) < 40 ) {
2023-06-21 02:07:21 +00:00
if ( ! this . clearView ( dest , path . getVec ( i + lookahead ) . add ( 0 , dy , 0 ) , false )
| | ! this . clearView ( dest , path . getVec ( i + lookahead ) , false ) ) {
2023-06-20 02:36:09 +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
continue ;
}
} else {
// but if it's far away, allow gaining altitude if we could lose it again by the time we get there
2023-06-21 02:07:21 +00:00
if ( ! this . clearView ( dest , path . getVec ( i ) , false ) ) {
2023-06-20 02:36:09 +00:00
continue ;
}
2023-06-11 07:33:35 +00:00
}
}
2023-06-18 00:36:29 +00:00
2023-06-22 23:22:40 +00:00
final double minAvoidance = Baritone . settings ( ) . elytraMinimumAvoidance . value ;
2023-06-21 02:07:21 +00:00
final Double growth = relaxation = = 2 ? null
2023-06-22 23:22:40 +00:00
: relaxation = = 0 ? 2 * minAvoidance : minAvoidance ;
2023-06-18 00:36:29 +00:00
2023-06-21 02:07:21 +00:00
if ( this . isHitboxClear ( start , dest , growth , isInLava ) ) {
2023-06-21 01:30:10 +00:00
// Yaw is trivial, just calculate the rotation required to face the destination
2023-06-20 02:36:09 +00:00
final float yaw = RotationUtils . calcRotationFromVec3d ( start , dest , ctx . playerRotations ( ) ) . getYaw ( ) ;
2023-06-18 18:23:41 +00:00
2023-06-26 21:04:05 +00:00
final Pair < Float , Boolean > pitch = this . solvePitch ( context , dest . subtract ( start ) , relaxation , isInLava ) ;
2023-06-20 02:36:09 +00:00
if ( pitch . first ( ) = = null ) {
2023-06-21 01:09:53 +00:00
solution = new Solution ( context , new Rotation ( yaw , ctx . playerRotations ( ) . getPitch ( ) ) , null , false , false ) ;
2023-06-20 02:36:09 +00:00
continue ;
}
2023-06-20 20:15:02 +00:00
// A solution was found with yaw AND pitch, so just immediately return it.
2023-06-21 01:30:10 +00:00
return new Solution ( context , new Rotation ( yaw , pitch . first ( ) ) , dest , true , pitch . second ( ) ) ;
2023-06-17 01:38:29 +00:00
}
}
2023-06-11 07:33:35 +00:00
}
}
2023-06-17 05:39:50 +00:00
}
2023-06-20 20:15:02 +00:00
return solution ;
2023-06-20 02:22:57 +00:00
}
2023-06-21 01:30:10 +00:00
private void tickUseFireworks ( final Vec3d start , final Vec3d goingTo , final boolean isBoosted , final boolean forceUseFirework ) {
2023-06-20 04:12:46 +00:00
if ( this . remainingSetBackTicks > 0 ) {
logDebug ( " waiting for elytraFireworkSetbackUseDelay: " + this . remainingSetBackTicks ) ;
2023-06-20 02:22:57 +00:00
return ;
}
2023-06-18 05:11:40 +00:00
final boolean useOnDescend = ! Baritone . settings ( ) . conserveFireworks . value | | ctx . player ( ) . posY < goingTo . y + 5 ;
2023-06-18 05:13:55 +00:00
final double currentSpeed = new Vec3d (
ctx . player ( ) . motionX ,
// ignore y component if we are BOTH below where we want to be AND descending
ctx . player ( ) . posY < goingTo . y ? Math . max ( 0 , ctx . player ( ) . motionY ) : ctx . player ( ) . motionY ,
ctx . player ( ) . motionZ
2023-06-20 02:22:57 +00:00
) . lengthSquared ( ) ;
2023-06-17 19:15:57 +00:00
2023-06-20 02:22:57 +00:00
final double elytraFireworkSpeed = Baritone . settings ( ) . elytraFireworkSpeed . value ;
2023-06-21 01:30:10 +00:00
if ( this . remainingFireworkTicks < = 0 & & ( forceUseFirework | | ( ! isBoosted
2023-06-18 05:11:40 +00:00
& & useOnDescend
2023-06-17 19:15:57 +00:00
& & ( ctx . player ( ) . posY < goingTo . y - 5 | | start . distanceTo ( new Vec3d ( goingTo . x + 0 . 5 , ctx . player ( ) . posY , goingTo . z + 0 . 5 ) ) > 5 ) // UGH!!!!!!!
2023-06-20 02:22:57 +00:00
& & currentSpeed < elytraFireworkSpeed * elytraFireworkSpeed ) )
2023-06-17 05:39:50 +00:00
) {
2023-06-19 17:32:25 +00:00
// Prioritize boosting fireworks over regular ones
// TODO: Take the minimum boost time into account?
if ( ! baritone . getInventoryBehavior ( ) . throwaway ( true , ElytraBehavior : : isBoostingFireworks ) & &
! baritone . getInventoryBehavior ( ) . throwaway ( true , ElytraBehavior : : isFireworks ) ) {
logDirect ( " no fireworks " ) ;
return ;
2023-06-18 05:24:10 +00:00
}
2023-06-19 06:09:43 +00:00
logDirect ( " attempting to use firework " + ( forceUseFirework ? " takeoff " : " " ) ) ;
2023-06-17 05:39:50 +00:00
ctx . playerController ( ) . processRightClick ( ctx . player ( ) , ctx . world ( ) , EnumHand . MAIN_HAND ) ;
2023-06-23 23:30:10 +00:00
this . minimumBoostTicks = 10 * ( 1 + getFireworkBoost ( ctx . player ( ) . getHeldItemMainhand ( ) ) . orElse ( 0 ) ) ;
2023-06-20 04:12:46 +00:00
this . remainingFireworkTicks = 10 ;
2023-06-11 07:33:35 +00:00
}
}
2023-06-21 01:09:53 +00:00
private final class SolverContext {
2023-06-21 02:07:21 +00:00
public final NetherPath path ;
2023-06-21 01:09:53 +00:00
public final int playerNear ;
public final Vec3d start ;
2023-06-26 18:55:18 +00:00
public final FireworkBoost boost ;
2023-06-25 04:44:08 +00:00
public final IAimProcessor aimProcessor ;
2023-06-21 01:09:53 +00:00
2023-06-25 04:44:08 +00:00
public SolverContext ( boolean async ) {
2023-06-21 01:30:10 +00:00
this . path = ElytraBehavior . this . pathManager . getPath ( ) ;
this . playerNear = ElytraBehavior . this . pathManager . getNear ( ) ;
this . start = ElytraBehavior . this . ctx . playerFeetAsVec ( ) ;
2023-06-26 18:55:18 +00:00
this . boost = new FireworkBoost (
ElytraBehavior . this . getAttachedFirework ( ) . orElse ( null ) ,
ElytraBehavior . this . minimumBoostTicks
) ;
2023-06-25 04:44:08 +00:00
ITickableAimProcessor aim = ElytraBehavior . this . baritone . getLookBehavior ( ) . getAimProcessor ( ) . fork ( ) ;
if ( async ) {
// async computation is done at the end of a tick, advance by 1 to prepare for the next tick
aim . advance ( 1 ) ;
}
this . aimProcessor = aim ;
2023-06-21 01:09:53 +00:00
}
@Override
public boolean equals ( Object o ) {
if ( this = = o ) {
return true ;
}
if ( o = = null | | o . getClass ( ) ! = SolverContext . class ) {
return false ;
}
SolverContext other = ( SolverContext ) o ;
return this . path = = other . path // Contents aren't modified, just compare by reference
& & this . playerNear = = other . playerNear
& & Objects . equals ( this . start , other . start )
2023-06-26 18:55:18 +00:00
& & Objects . equals ( this . boost , other . boost ) ;
}
}
private static final class FireworkBoost {
private final EntityFireworkRocket firework ;
private final int minimumBoostTicks ;
private final int maximumBoostTicks ;
public FireworkBoost ( final EntityFireworkRocket firework , final int minimumBoostTicks ) {
this . firework = firework ;
// this.lifetime = 10 * i + this.rand.nextInt(6) + this.rand.nextInt(7);
this . minimumBoostTicks = minimumBoostTicks ;
this . maximumBoostTicks = minimumBoostTicks + 11 ;
}
public boolean isBoosted ( ) {
return this . firework ! = null ;
}
public int getGuaranteedBoostTicks ( ) {
return this . isBoosted ( ) ? Math . max ( 0 , this . minimumBoostTicks - this . firework . ticksExisted ) : 0 ;
}
public int getMaximumBoostTicks ( ) {
return this . isBoosted ( ) ? Math . max ( 0 , this . maximumBoostTicks - this . firework . ticksExisted ) : 0 ;
}
@Override
public boolean equals ( Object o ) {
if ( this = = o ) {
return true ;
}
if ( o = = null | | o . getClass ( ) ! = FireworkBoost . class ) {
return false ;
}
FireworkBoost other = ( FireworkBoost ) o ;
return Objects . equals ( this . firework , other . firework )
& & this . minimumBoostTicks = = other . minimumBoostTicks
& & this . maximumBoostTicks = = other . maximumBoostTicks ;
2023-06-21 01:09:53 +00:00
}
}
2023-06-20 20:05:59 +00:00
private static final class Solution {
2023-06-21 01:09:53 +00:00
public final SolverContext context ;
2023-06-20 20:05:59 +00:00
public final Rotation rotation ;
2023-06-21 01:30:10 +00:00
public final Vec3d goingTo ;
2023-06-20 20:05:59 +00:00
public final boolean solvedPitch ;
public final boolean forceUseFirework ;
2023-06-21 01:30:10 +00:00
public Solution ( SolverContext context , Rotation rotation , Vec3d goingTo , boolean solvedPitch , boolean forceUseFirework ) {
2023-06-21 01:09:53 +00:00
this . context = context ;
2023-06-20 20:05:59 +00:00
this . rotation = rotation ;
2023-06-20 20:15:02 +00:00
this . goingTo = goingTo ;
2023-06-20 20:05:59 +00:00
this . solvedPitch = solvedPitch ;
this . forceUseFirework = forceUseFirework ;
}
}
2023-06-19 17:32:25 +00:00
private static boolean isFireworks ( final ItemStack itemStack ) {
if ( itemStack . getItem ( ) ! = Items . FIREWORKS ) {
return false ;
2023-06-18 05:24:10 +00:00
}
2023-06-19 17:32:25 +00:00
// If it has NBT data, make sure it won't cause us to explode.
2023-06-23 23:30:10 +00:00
final NBTTagCompound compound = itemStack . getSubCompound ( " Fireworks " ) ;
return compound = = null | | ! compound . hasKey ( " Explosions " ) ;
2023-06-18 05:24:10 +00:00
}
2023-06-19 17:32:25 +00:00
private static boolean isBoostingFireworks ( final ItemStack itemStack ) {
2023-06-23 23:30:10 +00:00
return getFireworkBoost ( itemStack ) . isPresent ( ) ;
}
private static OptionalInt getFireworkBoost ( final ItemStack itemStack ) {
if ( isFireworks ( itemStack ) ) {
final NBTTagCompound compound = itemStack . getSubCompound ( " Fireworks " ) ;
if ( compound ! = null & & compound . hasKey ( " Flight " ) ) {
return OptionalInt . of ( compound . getByte ( " Flight " ) ) ;
}
}
return OptionalInt . empty ( ) ;
2023-06-18 05:24:10 +00:00
}
2023-06-23 23:30:10 +00:00
private Optional < EntityFireworkRocket > getAttachedFirework ( ) {
2023-06-17 00:54:37 +00:00
return ctx . world ( ) . loadedEntityList . stream ( )
2023-06-23 23:30:10 +00:00
. filter ( x - > x instanceof EntityFireworkRocket )
. filter ( x - > Objects . equals ( ( ( IEntityFireworkRocket ) x ) . getBoostedEntity ( ) , ctx . player ( ) ) )
. map ( x - > ( EntityFireworkRocket ) x )
. findFirst ( ) ;
2023-06-11 07:33:35 +00:00
}
2023-06-21 02:07:21 +00:00
private boolean isHitboxClear ( final Vec3d start , final Vec3d dest , final Double growAmount , boolean ignoreLava ) {
2023-06-21 01:30:10 +00:00
if ( ! this . clearView ( start , dest , ignoreLava ) ) {
2023-06-18 00:36:29 +00:00
return false ;
}
if ( growAmount = = null ) {
return true ;
}
final AxisAlignedBB bb = ctx . player ( ) . getEntityBoundingBox ( ) . grow ( growAmount ) ;
2023-06-19 05:34:22 +00:00
2023-06-19 20:19:32 +00:00
final double ox = dest . x - start . x ;
final double oy = dest . y - start . y ;
final double oz = dest . z - start . z ;
final double [ ] src = new double [ ] {
bb . minX , bb . minY , bb . minZ ,
bb . minX , bb . minY , bb . maxZ ,
bb . minX , bb . maxY , bb . minZ ,
bb . minX , bb . maxY , bb . maxZ ,
bb . maxX , bb . minY , bb . minZ ,
bb . maxX , bb . minY , bb . maxZ ,
bb . maxX , bb . maxY , bb . minZ ,
bb . maxX , bb . maxY , bb . maxZ ,
} ;
final double [ ] dst = new double [ ] {
bb . minX + ox , bb . minY + oy , bb . minZ + oz ,
bb . minX + ox , bb . minY + oy , bb . maxZ + oz ,
bb . minX + ox , bb . maxY + oy , bb . minZ + oz ,
bb . minX + ox , bb . maxY + oy , bb . maxZ + oz ,
bb . maxX + ox , bb . minY + oy , bb . minZ + oz ,
bb . maxX + ox , bb . minY + oy , bb . maxZ + oz ,
bb . maxX + ox , bb . maxY + oy , bb . minZ + oz ,
bb . maxX + ox , bb . maxY + oy , bb . maxZ + oz ,
2023-06-18 00:36:29 +00:00
} ;
2023-06-25 05:11:08 +00:00
// Use non-batching method without early failure
if ( Baritone . settings ( ) . renderHitboxRaytraces . value ) {
boolean clear = true ;
for ( int i = 0 ; i < 8 ; i + + ) {
final Vec3d s = new Vec3d ( src [ i * 3 ] , src [ i * 3 + 1 ] , src [ i * 3 + 2 ] ) ;
final Vec3d d = new Vec3d ( dst [ i * 3 ] , dst [ i * 3 + 1 ] , dst [ i * 3 + 2 ] ) ;
// Don't forward ignoreLava since the batch call doesn't care about it
if ( ! this . clearView ( s , d , false ) ) {
clear = false ;
}
}
return clear ;
}
2023-06-20 02:36:09 +00:00
return this . context . raytrace ( 8 , src , dst , NetherPathfinderContext . Visibility . ALL ) ;
2023-06-11 07:33:35 +00:00
}
2023-06-19 21:58:06 +00:00
private boolean clearView ( Vec3d start , Vec3d dest , boolean ignoreLava ) {
final boolean clear ;
if ( ! ignoreLava ) {
2023-06-20 02:36:09 +00:00
// if start == dest then the cpp raytracer dies
2023-06-21 01:30:10 +00:00
clear = start . equals ( dest ) | | this . context . raytrace ( start , dest ) ;
2023-06-19 21:58:06 +00:00
} else {
2023-06-19 22:26:00 +00:00
clear = ctx . world ( ) . rayTraceBlocks ( start , dest , false , false , false ) = = null ;
2023-06-19 21:58:06 +00:00
}
2023-06-19 05:34:22 +00:00
if ( clear ) {
2023-06-21 01:09:53 +00:00
this . clearLines . add ( new Pair < > ( start , dest ) ) ;
2023-06-19 01:06:32 +00:00
return true ;
} else {
2023-06-21 01:09:53 +00:00
this . blockedLines . add ( new Pair < > ( start , dest ) ) ;
2023-06-19 01:06:32 +00:00
return false ;
}
2023-06-11 07:33:35 +00:00
}
2023-06-26 21:04:05 +00:00
private Pair < Float , Boolean > solvePitch ( SolverContext context , Vec3d goalDelta , int relaxation , boolean ignoreLava ) {
final int steps = relaxation < 2 ? context . boost . isBoosted ( ) ? 5 : Baritone . settings ( ) . elytraSimulationTicks . value : 3 ;
2023-06-26 18:55:18 +00:00
final Float pitch = this . solvePitch ( context , goalDelta , steps , relaxation = = 2 , context . boost . isBoosted ( ) , ignoreLava ) ;
2023-06-18 18:23:41 +00:00
if ( pitch ! = null ) {
return new Pair < > ( pitch , false ) ;
}
if ( Baritone . settings ( ) . experimentalTakeoff . value & & relaxation > 0 ) {
2023-06-25 04:44:08 +00:00
final Float usingFirework = this . solvePitch ( context , goalDelta , steps , relaxation = = 2 , true , ignoreLava ) ;
2023-06-18 18:23:41 +00:00
if ( usingFirework ! = null ) {
return new Pair < > ( usingFirework , true ) ;
}
}
return new Pair < > ( null , false ) ;
}
2023-06-26 21:12:56 +00:00
private Float solvePitch ( final SolverContext context , final Vec3d goalDelta , final int steps ,
final boolean desperate , final boolean firework , final boolean ignoreLava ) {
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
2023-06-22 17:50:06 +00:00
final Vec3d goalDirection = goalDelta . normalize ( ) ;
2023-06-25 04:44:08 +00:00
final float goodPitch = RotationUtils . calcRotationFromVec3d ( Vec3d . ZERO , goalDirection , ctx . playerRotations ( ) ) . getPitch ( ) ;
2023-06-11 07:33:35 +00:00
Float bestPitch = null ;
double bestDot = Double . NEGATIVE_INFINITY ;
2023-06-25 05:27:38 +00:00
List < Vec3d > bestLine = null ;
2023-06-22 17:50:06 +00:00
2023-06-25 04:44:08 +00:00
final float minPitch = desperate ? - 90 : Math . max ( goodPitch - Baritone . settings ( ) . elytraPitchRange . value , - 89 ) ;
final float maxPitch = desperate ? 90 : Math . min ( goodPitch + Baritone . settings ( ) . elytraPitchRange . value , 89 ) ;
2023-06-22 17:50:06 +00:00
2023-06-11 20:57:21 +00:00
for ( float pitch = minPitch ; pitch < = maxPitch ; pitch + + ) {
2023-06-26 21:21:15 +00:00
final List < Vec3d > displacement = this . simulate (
context . aimProcessor . fork ( ) ,
goalDelta ,
pitch ,
steps ,
firework ? Integer . MAX_VALUE : 0 ,
ignoreLava
) ;
if ( displacement = = null ) {
2023-06-26 21:12:56 +00:00
continue ;
2023-06-11 07:33:35 +00:00
}
2023-06-26 21:21:15 +00:00
final Vec3d result = displacement . get ( displacement . size ( ) - 1 ) ;
double goodness = goalDirection . dotProduct ( result . normalize ( ) ) ;
2023-06-11 07:33:35 +00:00
if ( goodness > bestDot ) {
bestDot = goodness ;
bestPitch = pitch ;
2023-06-26 21:21:15 +00:00
bestLine = displacement ;
2023-06-11 07:33:35 +00:00
}
}
2023-06-25 05:27:38 +00:00
if ( bestLine ! = null ) {
this . simulationLine = bestLine ;
}
2023-06-11 07:33:35 +00:00
return bestPitch ;
}
2023-06-26 21:21:15 +00:00
private List < Vec3d > simulate ( final ITickableAimProcessor aimProcessor , final Vec3d goalDelta , final float pitch ,
final int ticks , int ticksBoosted , final boolean ignoreLava ) {
2023-06-26 21:12:56 +00:00
Vec3d delta = goalDelta ;
Vec3d motion = ctx . playerMotion ( ) ;
AxisAlignedBB hitbox = ctx . player ( ) . getEntityBoundingBox ( ) ;
2023-06-26 21:21:15 +00:00
List < Vec3d > displacement = new ArrayList < > ( ) ;
displacement . add ( Vec3d . ZERO ) ;
2023-06-26 21:12:56 +00:00
for ( int i = 0 ; i < ticks ; i + + ) {
if ( MC_1_12_Collision_Fix . bonk ( ctx , hitbox ) ) {
return null ;
}
final Rotation rotation = aimProcessor . nextRotation (
RotationUtils . calcRotationFromVec3d ( Vec3d . ZERO , delta , ctx . playerRotations ( ) ) . withPitch ( pitch )
) ;
final Vec3d lookDirection = RotationUtils . calcLookDirectionFromRotation ( rotation ) ;
motion = step ( motion , lookDirection , rotation . getPitch ( ) ) ;
delta = delta . subtract ( motion ) ;
// Collision box while the player is in motion, with additional padding for safety
final AxisAlignedBB inMotion = hitbox . expand ( motion . x , motion . y , motion . z ) . grow ( 0 . 01 ) ;
for ( int x = MathHelper . floor ( inMotion . minX ) ; x < MathHelper . ceil ( inMotion . maxX ) ; x + + ) {
for ( int y = MathHelper . floor ( inMotion . minY ) ; y < MathHelper . ceil ( inMotion . maxY ) ; y + + ) {
for ( int z = MathHelper . floor ( inMotion . minZ ) ; z < MathHelper . ceil ( inMotion . maxZ ) ; z + + ) {
if ( ! this . passable ( x , y , z , ignoreLava ) ) {
return null ;
}
}
}
}
2023-06-26 21:21:15 +00:00
hitbox = hitbox . offset ( motion ) ;
displacement . add ( displacement . get ( displacement . size ( ) - 1 ) . add ( motion ) ) ;
2023-06-26 21:12:56 +00:00
2023-06-26 21:21:15 +00:00
if ( ticksBoosted - - > 0 ) {
2023-06-26 21:12:56 +00:00
// See EntityFireworkRocket
motion = motion . add (
lookDirection . x * 0 . 1 + ( lookDirection . x * 1 . 5 - motion . x ) * 0 . 5 ,
lookDirection . y * 0 . 1 + ( lookDirection . y * 1 . 5 - motion . y ) * 0 . 5 ,
lookDirection . z * 0 . 1 + ( lookDirection . z * 1 . 5 - motion . z ) * 0 . 5
) ;
}
}
2023-06-26 21:21:15 +00:00
return displacement ;
2023-06-26 21:12:56 +00:00
}
2023-06-26 20:35:40 +00:00
private static Vec3d step ( final Vec3d motion , final Vec3d lookDirection , final float pitch ) {
2023-06-11 07:33:35 +00:00
double motionX = motion . x ;
double motionY = motion . y ;
double motionZ = motion . z ;
2023-06-17 01:38:29 +00:00
2023-06-26 20:35:40 +00:00
float pitchRadians = pitch * RotationUtils . DEG_TO_RAD_F ;
2023-06-11 07:33:35 +00:00
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 ;
}
2023-06-21 23:59:44 +00:00
motionX * = 0 . 99f ;
motionY * = 0 . 98f ;
motionZ * = 0 . 99f ;
2023-06-11 07:33:35 +00:00
//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-21 01:09:53 +00:00
private boolean passable ( int x , int y , int z , boolean ignoreLava ) {
return passable ( this . bsi . get0 ( x , y , z ) , ignoreLava ) ;
}
private static boolean passable ( IBlockState state , boolean ignoreLava ) {
Material mat = state . getMaterial ( ) ;
return mat = = Material . AIR | | ( ignoreLava & & mat = = Material . LAVA ) ;
}
2023-06-22 23:07:11 +00:00
/ * *
* Minecraft 1 . 12 ' s pushOutOfBlocks logic doesn ' t account for players being able to fit under single block spaces ,
* so whenever the edge of a ceiling is encountered while elytra flying it tries to push the player out .
* /
private static final class MC_1_12_Collision_Fix {
public static boolean bonk ( final IPlayerContext ctx , final AxisAlignedBB aabb ) {
final Vec3d center = aabb . getCenter ( ) ;
final double width = ( double ) ctx . player ( ) . width * 0 . 35D ;
final double x = center . x ;
final double y = aabb . minY + 0 . 5D ;
final double z = center . z ;
return pushOutOfBlocks ( ctx , x - width , y , z + width )
| | pushOutOfBlocks ( ctx , x - width , y , z - width )
| | pushOutOfBlocks ( ctx , x + width , y , z - width )
| | pushOutOfBlocks ( ctx , x + width , y , z + width ) ;
}
private static boolean pushOutOfBlocks ( final IPlayerContext ctx , final double x , final double y , final double z ) {
final BlockPos pos = new BlockPos ( x , y , z ) ;
if ( isOpenBlockSpace ( ctx , pos ) ) {
return false ;
}
return isOpenBlockSpace ( ctx , pos . west ( ) )
| | isOpenBlockSpace ( ctx , pos . east ( ) )
| | isOpenBlockSpace ( ctx , pos . north ( ) )
| | isOpenBlockSpace ( ctx , pos . south ( ) ) ;
}
private static boolean isOpenBlockSpace ( IPlayerContext ctx , BlockPos pos ) {
return ! ctx . world ( ) . getBlockState ( pos ) . isNormalCube ( ) & & ! ctx . world ( ) . getBlockState ( pos . up ( ) ) . isNormalCube ( ) ;
}
}
2023-06-18 05:24:10 +00:00
}