2018-12-26 05:07:17 +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/>.
* /
package baritone.process ;
import baritone.Baritone ;
import baritone.api.pathing.goals.Goal ;
import baritone.api.pathing.goals.GoalBlock ;
import baritone.api.pathing.goals.GoalComposite ;
import baritone.api.pathing.goals.GoalGetToBlock ;
2019-01-16 04:07:06 +00:00
import baritone.api.process.IBuilderProcess ;
2018-12-26 05:07:17 +00:00
import baritone.api.process.PathingCommand ;
import baritone.api.process.PathingCommandType ;
2019-02-07 00:22:40 +00:00
import baritone.api.utils.* ;
2019-01-09 04:45:02 +00:00
import baritone.api.utils.input.Input ;
2018-12-26 05:07:17 +00:00
import baritone.pathing.movement.CalculationContext ;
2019-02-07 00:22:40 +00:00
import baritone.pathing.movement.Movement ;
2019-01-09 04:45:02 +00:00
import baritone.pathing.movement.MovementHelper ;
2018-12-26 05:07:17 +00:00
import baritone.utils.BaritoneProcessHelper ;
2019-02-07 00:22:40 +00:00
import baritone.utils.BlockStateInterface ;
2018-12-26 05:07:17 +00:00
import baritone.utils.PathingCommandContext ;
2019-01-16 19:45:32 +00:00
import baritone.utils.schematic.Schematic ;
2018-12-26 05:07:17 +00:00
import net.minecraft.block.state.IBlockState ;
import net.minecraft.client.Minecraft ;
import net.minecraft.init.Blocks ;
import net.minecraft.item.ItemBlock ;
import net.minecraft.item.ItemStack ;
import net.minecraft.nbt.CompressedStreamTools ;
import net.minecraft.nbt.NBTTagCompound ;
import net.minecraft.util.EnumFacing ;
2019-01-09 04:45:02 +00:00
import net.minecraft.util.Tuple ;
2019-02-07 00:22:40 +00:00
import net.minecraft.util.math.* ;
2018-12-26 05:07:17 +00:00
import java.io.File ;
2018-12-27 18:06:10 +00:00
import java.io.FileInputStream ;
2018-12-26 05:07:17 +00:00
import java.io.IOException ;
2019-01-09 04:45:02 +00:00
import java.util.* ;
import java.util.stream.Collectors ;
2018-12-26 05:07:17 +00:00
import static baritone.api.pathing.movement.ActionCosts.COST_INF ;
2019-01-16 04:07:06 +00:00
public class BuilderProcess extends BaritoneProcessHelper implements IBuilderProcess {
2018-12-26 05:07:17 +00:00
public BuilderProcess ( Baritone baritone ) {
2019-01-20 06:12:54 +00:00
super ( baritone , 0 ) ;
2018-12-26 05:07:17 +00:00
}
private HashSet < BetterBlockPos > incorrectPositions ;
private String name ;
private ISchematic schematic ;
private Vec3i origin ;
2019-02-07 00:22:40 +00:00
private int ticks ;
2018-12-26 05:07:17 +00:00
2019-02-07 06:10:52 +00:00
public boolean build ( String schematicFile , BlockPos origin ) {
2018-12-26 05:07:17 +00:00
File file = new File ( new File ( Minecraft . getMinecraft ( ) . gameDir , " schematics " ) , schematicFile ) ;
2018-12-27 18:06:10 +00:00
System . out . println ( file + " " + file . exists ( ) ) ;
2019-02-07 06:10:52 +00:00
return build ( schematicFile , file , origin ) ;
2019-01-16 04:07:06 +00:00
}
@Override
public void build ( String name , ISchematic schematic , Vec3i origin ) {
this . name = name ;
this . schematic = schematic ;
this . origin = origin ;
}
2018-12-27 18:06:10 +00:00
2019-01-16 04:07:06 +00:00
@Override
public boolean build ( String name , File schematic , Vec3i origin ) {
2018-12-26 05:07:17 +00:00
NBTTagCompound tag ;
2019-01-16 04:07:06 +00:00
try ( FileInputStream fileIn = new FileInputStream ( schematic ) ) {
2018-12-27 18:06:10 +00:00
tag = CompressedStreamTools . readCompressed ( fileIn ) ;
2018-12-26 05:07:17 +00:00
} catch ( IOException e ) {
e . printStackTrace ( ) ;
return false ;
}
if ( tag = = null ) {
return false ;
}
2019-01-16 04:07:06 +00:00
build ( name , parse ( tag ) , origin ) ;
2018-12-26 05:07:17 +00:00
return true ;
}
private static ISchematic parse ( NBTTagCompound schematic ) {
2019-01-09 04:45:02 +00:00
return new Schematic ( schematic ) ;
2018-12-26 05:07:17 +00:00
}
@Override
public boolean isActive ( ) {
return schematic ! = null ;
}
2019-01-15 03:49:26 +00:00
public IBlockState placeAt ( int x , int y , int z ) {
2019-01-13 03:51:21 +00:00
if ( ! isActive ( ) ) {
return null ;
}
if ( ! schematic . inSchematic ( x - origin . getX ( ) , y - origin . getY ( ) , z - origin . getZ ( ) ) ) {
return null ;
}
IBlockState state = schematic . desiredState ( x - origin . getX ( ) , y - origin . getY ( ) , z - origin . getZ ( ) ) ;
if ( state . getBlock ( ) = = Blocks . AIR ) {
return null ;
}
2019-01-15 03:49:26 +00:00
return state ;
2019-01-13 03:51:21 +00:00
}
2019-01-09 04:45:02 +00:00
public Optional < Tuple < BetterBlockPos , Rotation > > toBreakNearPlayer ( BuilderCalculationContext bcc ) {
BetterBlockPos center = ctx . playerFeet ( ) ;
for ( int dx = - 5 ; dx < = 5 ; dx + + ) {
for ( int dy = 0 ; dy < = 5 ; dy + + ) {
for ( int dz = - 5 ; dz < = 5 ; dz + + ) {
int x = center . x + dx ;
int y = center . y + dy ;
int z = center . z + dz ;
IBlockState desired = bcc . getSchematic ( x , y , z ) ;
if ( desired = = null ) {
continue ; // irrelevant
}
IBlockState curr = bcc . bsi . get0 ( x , y , z ) ;
if ( curr . getBlock ( ) ! = Blocks . AIR & & ! valid ( curr , desired ) ) {
BetterBlockPos pos = new BetterBlockPos ( x , y , z ) ;
Optional < Rotation > rot = RotationUtils . reachable ( ctx . player ( ) , pos , ctx . playerController ( ) . getBlockReachDistance ( ) ) ;
if ( rot . isPresent ( ) ) {
return Optional . of ( new Tuple < > ( pos , rot . get ( ) ) ) ;
}
}
}
}
}
return Optional . empty ( ) ;
}
2019-02-07 00:22:40 +00:00
public class Placement {
final int hotbarSelection ;
final BlockPos placeAgainst ;
final EnumFacing side ;
final Rotation rot ;
public Placement ( int hotbarSelection , BlockPos placeAgainst , EnumFacing side , Rotation rot ) {
this . hotbarSelection = hotbarSelection ;
this . placeAgainst = placeAgainst ;
this . side = side ;
this . rot = rot ;
}
}
2019-02-07 21:59:37 +00:00
public Optional < Placement > searchForPlacables ( BuilderCalculationContext bcc , List < IBlockState > desirableOnHotbar ) {
2019-02-07 00:22:40 +00:00
BetterBlockPos center = ctx . playerFeet ( ) ;
for ( int dx = - 5 ; dx < = 5 ; dx + + ) {
for ( int dy = - 5 ; dy < = 1 ; dy + + ) {
for ( int dz = - 5 ; dz < = 5 ; dz + + ) {
int x = center . x + dx ;
int y = center . y + dy ;
int z = center . z + dz ;
IBlockState desired = bcc . getSchematic ( x , y , z ) ;
if ( desired = = null ) {
continue ; // irrelevant
}
IBlockState curr = bcc . bsi . get0 ( x , y , z ) ;
if ( MovementHelper . isReplacable ( x , y , z , curr , bcc . bsi ) & & ! valid ( curr , desired ) ) {
if ( dy = = 1 & & bcc . bsi . get0 ( x , y + 1 , z ) . getBlock ( ) = = Blocks . AIR ) {
continue ;
}
2019-02-07 21:59:37 +00:00
desirableOnHotbar . add ( desired ) ;
2019-02-07 00:22:40 +00:00
Optional < Placement > opt = possibleToPlace ( desired , x , y , z , bcc . bsi ) ;
if ( opt . isPresent ( ) ) {
return opt ;
}
}
}
}
}
return Optional . empty ( ) ;
}
public Optional < Placement > possibleToPlace ( IBlockState toPlace , int x , int y , int z , BlockStateInterface bsi ) {
for ( EnumFacing against : EnumFacing . values ( ) ) {
BetterBlockPos placeAgainstPos = new BetterBlockPos ( x , y , z ) . offset ( against ) ;
IBlockState placeAgainstState = bsi . get0 ( placeAgainstPos ) ;
if ( MovementHelper . isReplacable ( placeAgainstPos . x , placeAgainstPos . y , placeAgainstPos . z , placeAgainstState , bsi ) ) {
continue ;
}
if ( ! ctx . world ( ) . mayPlace ( toPlace . getBlock ( ) , new BetterBlockPos ( x , y , z ) , false , against , null ) ) {
continue ;
}
AxisAlignedBB aabb = placeAgainstState . getBoundingBox ( ctx . world ( ) , placeAgainstPos ) ;
for ( Vec3d placementMultiplier : aabbSideMultipliers ( against ) ) {
double placeX = placeAgainstPos . x + aabb . minX * placementMultiplier . x + aabb . maxX * ( 1 - placementMultiplier . x ) ;
double placeY = placeAgainstPos . y + aabb . minY * placementMultiplier . y + aabb . maxY * ( 1 - placementMultiplier . y ) ;
double placeZ = placeAgainstPos . z + aabb . minZ * placementMultiplier . z + aabb . maxZ * ( 1 - placementMultiplier . z ) ;
2019-02-17 04:49:07 +00:00
Rotation rot = RotationUtils . calcRotationFromVec3d ( ctx . playerHead ( ) , new Vec3d ( placeX , placeY , placeZ ) , ctx . playerRotations ( ) ) ;
2019-02-07 00:22:40 +00:00
RayTraceResult result = RayTraceUtils . rayTraceTowards ( ctx . player ( ) , rot , ctx . playerController ( ) . getBlockReachDistance ( ) ) ;
if ( result ! = null & & result . typeOfHit = = RayTraceResult . Type . BLOCK & & result . getBlockPos ( ) . equals ( placeAgainstPos ) & & result . sideHit = = against . getOpposite ( ) ) {
OptionalInt hotbar = hasAnyItemThatWouldPlace ( toPlace , result , rot ) ;
if ( hotbar . isPresent ( ) ) {
return Optional . of ( new Placement ( hotbar . getAsInt ( ) , placeAgainstPos , against . getOpposite ( ) , rot ) ) ;
}
}
}
}
return Optional . empty ( ) ;
}
public OptionalInt hasAnyItemThatWouldPlace ( IBlockState desired , RayTraceResult result , Rotation rot ) {
for ( int i = 0 ; i < 9 ; i + + ) {
ItemStack stack = ctx . player ( ) . inventory . mainInventory . get ( i ) ;
if ( stack . isEmpty ( ) | | ! ( stack . getItem ( ) instanceof ItemBlock ) ) {
continue ;
}
float originalYaw = ctx . player ( ) . rotationYaw ;
float originalPitch = ctx . player ( ) . rotationPitch ;
// the state depends on the facing of the player sometimes
ctx . player ( ) . rotationYaw = rot . getYaw ( ) ;
ctx . player ( ) . rotationPitch = rot . getPitch ( ) ;
IBlockState wouldBePlaced = ( ( ItemBlock ) stack . getItem ( ) ) . getBlock ( ) . getStateForPlacement (
ctx . world ( ) ,
result . getBlockPos ( ) . offset ( result . sideHit ) ,
result . sideHit ,
( float ) result . hitVec . x - result . getBlockPos ( ) . getX ( ) , // as in PlayerControllerMP
( float ) result . hitVec . y - result . getBlockPos ( ) . getY ( ) ,
( float ) result . hitVec . z - result . getBlockPos ( ) . getZ ( ) ,
stack . getItem ( ) . getMetadata ( stack . getMetadata ( ) ) ,
ctx . player ( )
) ;
ctx . player ( ) . rotationYaw = originalYaw ;
ctx . player ( ) . rotationPitch = originalPitch ;
if ( valid ( wouldBePlaced , desired ) ) {
return OptionalInt . of ( i ) ;
}
}
return OptionalInt . empty ( ) ;
}
private static Vec3d [ ] aabbSideMultipliers ( EnumFacing side ) {
switch ( side ) {
case UP :
return new Vec3d [ ] { new Vec3d ( 0 . 5 , 1 , 0 . 5 ) } ;
case DOWN :
return new Vec3d [ ] { new Vec3d ( 0 . 5 , 0 , 0 . 5 ) } ;
case NORTH :
case SOUTH :
case EAST :
case WEST :
double x = side . getXOffset ( ) = = 0 ? 0 . 5 : ( 1 + side . getXOffset ( ) ) / 2D ;
double z = side . getZOffset ( ) = = 0 ? 0 . 5 : ( 1 + side . getZOffset ( ) ) / 2D ;
return new Vec3d [ ] { new Vec3d ( x , 0 . 25 , z ) , new Vec3d ( x , 0 . 75 , z ) } ;
default : // null
throw new NullPointerException ( ) ;
}
}
2018-12-26 05:07:17 +00:00
@Override
public PathingCommand onTick ( boolean calcFailed , boolean isSafeToCancel ) {
// TODO somehow tell inventorybehavior what we'd like to have on the hotbar
// perhaps take the 16 closest positions in incorrectPositions to ctx.playerFeet that aren't desired to be air, and then snag the top 4 most common block states, then request those on the hotbar
// this will work as is, but it'll be trashy
// need to iterate over incorrectPositions and see which ones we can "correct" from our current standing position
2019-01-09 04:45:02 +00:00
2019-01-13 03:51:21 +00:00
BuilderCalculationContext bcc = new BuilderCalculationContext ( ) ;
2019-01-09 04:45:02 +00:00
if ( ! recalc ( bcc ) ) {
logDirect ( " Done building " ) ;
onLostControl ( ) ;
return null ;
}
2019-02-20 06:50:03 +00:00
trim ( bcc ) ;
2019-02-07 00:22:40 +00:00
if ( baritone . getInputOverrideHandler ( ) . isInputForcedDown ( Input . CLICK_LEFT ) ) {
ticks = 5 ;
} else {
ticks - - ;
}
2019-01-09 04:45:02 +00:00
Optional < Tuple < BetterBlockPos , Rotation > > toBreak = toBreakNearPlayer ( bcc ) ;
baritone . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
if ( toBreak . isPresent ( ) & & isSafeToCancel & & ctx . player ( ) . onGround ) {
// we'd like to pause to break this block
// only change look direction if it's safe (don't want to fuck up an in progress parkour for example
Rotation rot = toBreak . get ( ) . getSecond ( ) ;
BetterBlockPos pos = toBreak . get ( ) . getFirst ( ) ;
baritone . getLookBehavior ( ) . updateTarget ( rot , true ) ;
MovementHelper . switchToBestToolFor ( ctx , bcc . get ( pos ) ) ;
2019-02-07 00:22:40 +00:00
if ( Objects . equals ( ctx . objectMouseOver ( ) . getBlockPos ( ) , pos ) | | ctx . playerRotations ( ) . isReallyCloseTo ( rot ) ) {
2019-01-09 04:45:02 +00:00
baritone . getInputOverrideHandler ( ) . setInputForceState ( Input . CLICK_LEFT , true ) ;
}
return new PathingCommand ( null , PathingCommandType . REQUEST_PAUSE ) ;
}
2019-02-07 21:59:37 +00:00
List < IBlockState > desirableOnHotbar = new ArrayList < > ( ) ;
Optional < Placement > toPlace = searchForPlacables ( bcc , desirableOnHotbar ) ;
2019-02-07 00:22:40 +00:00
if ( toPlace . isPresent ( ) & & isSafeToCancel & & ctx . player ( ) . onGround & & ticks < = 0 ) {
Rotation rot = toPlace . get ( ) . rot ;
baritone . getLookBehavior ( ) . updateTarget ( rot , true ) ;
ctx . player ( ) . inventory . currentItem = toPlace . get ( ) . hotbarSelection ;
baritone . getInputOverrideHandler ( ) . setInputForceState ( Input . SNEAK , true ) ;
if ( ( Objects . equals ( ctx . objectMouseOver ( ) . getBlockPos ( ) , toPlace . get ( ) . placeAgainst ) & & ctx . objectMouseOver ( ) . sideHit . equals ( toPlace . get ( ) . side ) ) | | ctx . playerRotations ( ) . isReallyCloseTo ( rot ) ) {
baritone . getInputOverrideHandler ( ) . setInputForceState ( Input . CLICK_RIGHT , true ) ;
}
return new PathingCommand ( null , PathingCommandType . REQUEST_PAUSE ) ;
}
2019-01-09 04:45:02 +00:00
2019-02-07 21:59:37 +00:00
List < IBlockState > approxPlacable = placable ( 36 ) ;
if ( Baritone . settings ( ) . allowInventory . get ( ) ) {
ArrayList < Integer > usefulSlots = new ArrayList < > ( ) ;
List < IBlockState > noValidHotbarOption = new ArrayList < > ( ) ;
outer :
for ( IBlockState desired : desirableOnHotbar ) {
for ( int i = 0 ; i < 9 ; i + + ) {
if ( valid ( approxPlacable . get ( i ) , desired ) ) {
usefulSlots . add ( i ) ;
continue outer ;
}
}
noValidHotbarOption . add ( desired ) ;
}
outer :
for ( int i = 9 ; i < 36 ; i + + ) {
for ( IBlockState desired : noValidHotbarOption ) {
if ( valid ( approxPlacable . get ( i ) , desired ) ) {
baritone . getInventoryBehavior ( ) . attemptToPutOnHotbar ( i , usefulSlots : : contains ) ;
break outer ;
}
}
}
}
Goal goal = assemble ( bcc , approxPlacable . subList ( 0 , 9 ) ) ;
2019-01-09 23:07:06 +00:00
if ( goal = = null ) {
2019-02-07 21:59:37 +00:00
goal = assemble ( bcc , approxPlacable ) ; // we're far away, so assume that we have our whole inventory to recalculate placable properly
if ( goal = = null ) {
logDirect ( " Unable to do it =( " ) ;
onLostControl ( ) ;
return null ;
}
2019-01-09 04:45:02 +00:00
}
2019-01-09 23:07:06 +00:00
return new PathingCommandContext ( goal , PathingCommandType . FORCE_REVALIDATE_GOAL_AND_PATH , bcc ) ;
2019-01-09 04:45:02 +00:00
}
public boolean recalc ( BuilderCalculationContext bcc ) {
if ( incorrectPositions = = null ) {
incorrectPositions = new HashSet < > ( ) ;
fullRecalc ( bcc ) ;
if ( incorrectPositions . isEmpty ( ) ) {
return false ;
}
}
recalcNearby ( bcc ) ;
if ( incorrectPositions . isEmpty ( ) ) {
fullRecalc ( bcc ) ;
}
return ! incorrectPositions . isEmpty ( ) ;
}
2019-02-20 06:50:03 +00:00
public void trim ( BuilderCalculationContext bcc ) {
HashSet < BetterBlockPos > copy = new HashSet < > ( incorrectPositions ) ;
copy . removeIf ( pos - > pos . distanceSq ( ctx . player ( ) . posX , ctx . player ( ) . posY , ctx . player ( ) . posZ ) > 200 ) ;
if ( ! copy . isEmpty ( ) ) {
incorrectPositions = copy ;
}
}
2019-01-09 04:45:02 +00:00
public void recalcNearby ( BuilderCalculationContext bcc ) {
BetterBlockPos center = ctx . playerFeet ( ) ;
for ( int dx = - 5 ; dx < = 5 ; dx + + ) {
for ( int dy = - 5 ; dy < = 5 ; dy + + ) {
for ( int dz = - 5 ; dz < = 5 ; dz + + ) {
int x = center . x + dx ;
int y = center . y + dy ;
int z = center . z + dz ;
IBlockState desired = bcc . getSchematic ( x , y , z ) ;
if ( desired ! = null ) {
// we care about this position
if ( valid ( bcc . bsi . get0 ( x , y , z ) , desired ) ) {
incorrectPositions . remove ( new BetterBlockPos ( x , y , z ) ) ;
} else {
incorrectPositions . add ( new BetterBlockPos ( x , y , z ) ) ;
}
}
}
}
}
}
public void fullRecalc ( BuilderCalculationContext bcc ) {
incorrectPositions = new HashSet < > ( ) ;
for ( int y = 0 ; y < schematic . heightY ( ) ; y + + ) {
for ( int z = 0 ; z < schematic . lengthZ ( ) ; z + + ) {
for ( int x = 0 ; x < schematic . widthX ( ) ; x + + ) {
if ( schematic . inSchematic ( x , y , z ) ) {
if ( ! valid ( bcc . bsi . get0 ( x + origin . getX ( ) , y + origin . getY ( ) , z + origin . getZ ( ) ) , schematic . desiredState ( x , y , z ) ) ) {
incorrectPositions . add ( new BetterBlockPos ( x + origin . getX ( ) , y + origin . getY ( ) , z + origin . getZ ( ) ) ) ;
}
}
}
}
}
2018-12-26 05:07:17 +00:00
}
2019-02-07 21:59:37 +00:00
private Goal assemble ( BuilderCalculationContext bcc , List < IBlockState > approxPlacable ) {
2019-01-09 04:45:02 +00:00
List < BetterBlockPos > placable = incorrectPositions . stream ( ) . filter ( pos - > bcc . bsi . get0 ( pos ) . getBlock ( ) = = Blocks . AIR & & approxPlacable . contains ( bcc . getSchematic ( pos . x , pos . y , pos . z ) ) ) . collect ( Collectors . toList ( ) ) ;
2019-01-09 23:07:06 +00:00
Goal [ ] toBreak = incorrectPositions . stream ( ) . filter ( pos - > bcc . bsi . get0 ( pos ) . getBlock ( ) ! = Blocks . AIR ) . map ( GoalBreak : : new ) . toArray ( Goal [ ] : : new ) ;
2019-02-07 00:22:40 +00:00
Goal [ ] toPlace = placable . stream ( ) . filter ( pos - > ! placable . contains ( pos . down ( ) ) & & ! placable . contains ( pos . down ( 2 ) ) ) . map ( pos - > placementgoal ( pos , bcc ) ) . toArray ( Goal [ ] : : new ) ;
2019-01-09 23:07:06 +00:00
if ( toPlace . length ! = 0 ) {
return new JankyGoalComposite ( new GoalComposite ( toPlace ) , new GoalComposite ( toBreak ) ) ;
}
if ( toBreak . length = = 0 ) {
return null ;
}
return new GoalComposite ( toBreak ) ;
}
public static class JankyGoalComposite implements Goal {
private final Goal primary ;
private final Goal fallback ;
public JankyGoalComposite ( Goal primary , Goal fallback ) {
this . primary = primary ;
this . fallback = fallback ;
}
@Override
public boolean isInGoal ( int x , int y , int z ) {
return primary . isInGoal ( x , y , z ) | | fallback . isInGoal ( x , y , z ) ;
}
@Override
public double heuristic ( int x , int y , int z ) {
return primary . heuristic ( x , y , z ) ;
2019-01-09 04:45:02 +00:00
}
}
public static class GoalBreak extends GoalGetToBlock {
public GoalBreak ( BlockPos pos ) {
super ( pos ) ;
}
@Override
public boolean isInGoal ( int x , int y , int z ) {
// can't stand right on top of a block, that might not work (what if it's unsupported, can't break then)
2019-01-09 22:36:44 +00:00
if ( y > this . y ) {
2019-01-09 04:45:02 +00:00
return false ;
}
// but any other adjacent works for breaking, including inside or below
return super . isInGoal ( x , y , z ) ;
}
2018-12-26 05:07:17 +00:00
}
2019-02-07 00:22:40 +00:00
public Goal placementgoal ( BlockPos pos , BuilderCalculationContext bcc ) {
2019-02-20 16:08:52 +00:00
if ( ctx . world ( ) . getBlockState ( pos ) . getBlock ( ) ! = Blocks . AIR ) {
return new GoalPlace ( pos ) ;
}
2019-02-07 00:22:40 +00:00
boolean allowSameLevel = ctx . world ( ) . getBlockState ( pos . up ( ) ) . getBlock ( ) ! = Blocks . AIR ;
for ( EnumFacing facing : Movement . HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP ) {
if ( MovementHelper . canPlaceAgainst ( ctx , pos . offset ( facing ) ) & & ctx . world ( ) . mayPlace ( bcc . getSchematic ( pos . getX ( ) , pos . getY ( ) , pos . getZ ( ) ) . getBlock ( ) , pos , false , facing , null ) ) {
return new GoalAdjacent ( pos , allowSameLevel ) ;
}
}
return new GoalPlace ( pos ) ;
}
public static class GoalAdjacent extends GoalGetToBlock {
boolean allowSameLevel ;
public GoalAdjacent ( BlockPos pos , boolean allowSameLevel ) {
super ( pos ) ;
this . allowSameLevel = allowSameLevel ;
}
public boolean isInGoal ( int x , int y , int z ) {
if ( x = = this . x & & y = = this . y & & z = = this . z ) {
return false ;
}
if ( ! allowSameLevel & & y = = this . y - 1 ) {
return false ;
}
if ( y < this . y - 1 ) {
return false ;
}
return super . isInGoal ( x , y , z ) ;
}
public double heuristic ( int x , int y , int z ) {
// prioritize lower y coordinates
return this . y * 100 + super . heuristic ( x , y , z ) ;
}
}
2019-01-09 22:36:44 +00:00
public static class GoalPlace extends GoalBlock {
public GoalPlace ( BlockPos placeAt ) {
super ( placeAt . up ( ) ) ;
}
public double heuristic ( int x , int y , int z ) {
// prioritize lower y coordinates
return this . y * 100 + super . heuristic ( x , y , z ) ;
}
}
2018-12-26 05:07:17 +00:00
@Override
public void onLostControl ( ) {
incorrectPositions = null ;
name = null ;
schematic = null ;
}
@Override
public String displayName ( ) {
return " Building " + name ;
}
2019-02-07 21:59:37 +00:00
public List < IBlockState > placable ( int size ) {
2018-12-26 05:07:17 +00:00
List < IBlockState > result = new ArrayList < > ( ) ;
2019-02-07 21:59:37 +00:00
for ( int i = 0 ; i < size ; i + + ) {
2018-12-26 05:07:17 +00:00
ItemStack stack = ctx . player ( ) . inventory . mainInventory . get ( i ) ;
if ( stack . isEmpty ( ) | | ! ( stack . getItem ( ) instanceof ItemBlock ) ) {
result . add ( Blocks . AIR . getDefaultState ( ) ) ;
continue ;
}
// <toxic cloud>
result . add ( ( ( ItemBlock ) stack . getItem ( ) ) . getBlock ( ) . getStateForPlacement ( ctx . world ( ) , ctx . playerFeet ( ) , EnumFacing . UP , ( float ) ctx . player ( ) . posX , ( float ) ctx . player ( ) . posY , ( float ) ctx . player ( ) . posZ , stack . getItem ( ) . getMetadata ( stack . getMetadata ( ) ) , ctx . player ( ) ) ) ;
// </toxic cloud>
}
return result ;
}
2019-01-09 04:45:02 +00:00
public boolean valid ( IBlockState current , IBlockState desired ) {
// TODO more complicated comparison logic I guess
return desired = = null | | current . equals ( desired ) ;
}
2018-12-26 05:07:17 +00:00
public class BuilderCalculationContext extends CalculationContext {
private final List < IBlockState > placable ;
private final ISchematic schematic ;
private final int originX ;
private final int originY ;
private final int originZ ;
2019-01-13 03:51:21 +00:00
public BuilderCalculationContext ( ) {
2018-12-26 05:07:17 +00:00
super ( BuilderProcess . this . baritone , true ) ; // wew lad
2019-02-07 21:59:37 +00:00
this . placable = placable ( 9 ) ;
2019-01-13 03:51:21 +00:00
this . schematic = BuilderProcess . this . schematic ;
this . originX = origin . getX ( ) ;
this . originY = origin . getY ( ) ;
this . originZ = origin . getZ ( ) ;
2019-01-09 22:36:44 +00:00
this . jumpPenalty + = 10 ;
2019-01-09 23:42:49 +00:00
this . backtrackCostFavoringCoefficient = 1 ;
2018-12-26 05:07:17 +00:00
}
private IBlockState getSchematic ( int x , int y , int z ) {
if ( schematic . inSchematic ( x - originX , y - originY , z - originZ ) ) {
return schematic . desiredState ( x - originX , y - originY , z - originZ ) ;
} else {
return null ;
}
}
@Override
public double costOfPlacingAt ( int x , int y , int z ) {
if ( isPossiblyProtected ( x , y , z ) | | ! worldBorder . canPlaceAt ( x , z ) ) { // make calculation fail properly if we can't build
return COST_INF ;
}
IBlockState sch = getSchematic ( x , y , z ) ;
if ( sch ! = null ) {
// TODO this can return true even when allowPlace is off.... is that an issue?
2019-01-09 04:45:02 +00:00
if ( sch . getBlock ( ) = = Blocks . AIR ) {
// we want this to be air, but they're asking if they can place here
// this won't be a schematic block, this will be a throwaway
return placeBlockCost * 2 ; // we're going to have to break it eventually
}
2018-12-26 05:07:17 +00:00
if ( placable . contains ( sch ) ) {
return 0 ; // thats right we gonna make it FREE to place a block where it should go in a structure
// no place block penalty at all 😎
// i'm such an idiot that i just tried to copy and paste the epic gamer moment emoji too
// get added to unicode when?
}
if ( ! hasThrowaway ) {
return COST_INF ;
}
2019-01-09 04:45:02 +00:00
// we want it to be something that we don't have
// even more of a pain to place something wrong
return placeBlockCost * 3 ;
2018-12-26 05:07:17 +00:00
} else {
if ( hasThrowaway ) {
return placeBlockCost ;
} else {
return COST_INF ;
}
}
}
@Override
2019-01-09 04:45:02 +00:00
public double breakCostMultiplierAt ( int x , int y , int z ) {
2018-12-26 05:07:17 +00:00
if ( ! allowBreak | | isPossiblyProtected ( x , y , z ) ) {
2019-01-09 04:45:02 +00:00
return COST_INF ;
2018-12-26 05:07:17 +00:00
}
IBlockState sch = getSchematic ( x , y , z ) ;
if ( sch ! = null ) {
if ( sch . getBlock ( ) = = Blocks . AIR ) {
// it should be air
// regardless of current contents, we can break it
2019-01-09 04:45:02 +00:00
return 1 ;
2018-12-26 05:07:17 +00:00
}
// it should be a real block
// is it already that block?
2019-01-09 04:45:02 +00:00
if ( valid ( bsi . get0 ( x , y , z ) , sch ) ) {
return 3 ;
} else {
// can break if it's wrong
// would be great to return less than 1 here, but that would actually make the cost calculation messed up
// since we're breaking a block, if we underestimate the cost, then it'll fail when it really takes the correct amount of time
return 1 ;
}
2018-12-26 05:07:17 +00:00
// TODO do blocks in render distace only?
// TODO allow breaking blocks that we have a tool to harvest and immediately place back?
} else {
2019-01-09 04:45:02 +00:00
return 1 ; // why not lol
2018-12-26 05:07:17 +00:00
}
}
}
}