Posted February 22, 20223 yr I'm currently making a mob that eats crops. I've got it kind of working, but the behavior isn't quite the way I want it to be. My goal is to get the mob to eat all crops nearby without stopping. Currently, my mob will move up to a crop, eat it, and then be idle for awhile as neither the move to crop goal nor eat crop goal are being started. Is there a way to get make these goals fire more often? MoveToCropGoal: public class MoveToCropGoal extends MoveToBlockGoal { private static final int GIVE_UP_TICKS = 600; private boolean shouldStop; public MoveToCropGoal(PathfinderMob mob) { super(mob, 1.0, 100); } @Override protected boolean isValidTarget(LevelReader levelReader, BlockPos blockPos) { return levelReader.getBlockState(blockPos).getBlock() instanceof CropBlock; } @Override public boolean shouldRecalculatePath() { return this.tryTicks % 10 == 0; } @Override public void start() { super.start(); Main.LOGGER.info("Starting to move towards crop"); shouldStop = false; } @Override public void stop() { this.shouldStop = true; } @Override protected boolean isReachedTarget() { return super.isReachedTarget() || Utils.getSurroundingBlockPoses(this.mob).stream().anyMatch(blockPos -> Utils.blockPosIsCrop(this.mob.level, blockPos)); } @Override public boolean canContinueToUse() { boolean canContinue = !shouldStop && !this.isReachedTarget() && this.tryTicks <= GIVE_UP_TICKS && this.isValidTarget(this.mob.level, this.blockPos); Main.LOGGER.info("Can continue to move towards crop: " + canContinue); return canContinue; } } EatCropGoal: public class EatCropGoal extends EatBlockGoal { protected final Mob mob; protected final Level level; private final int MAX_TICKS = 10; private int ticks; private boolean hasEaten; public EatCropGoal(Mob mob) { super(mob); this.mob = mob; this.level = mob.level; } @Override public boolean canUse() { for (BlockPos blockPos : Utils.getSurroundingBlockPoses(this.mob)) { if (Utils.blockPosIsCrop(this.level, blockPos)) { return true; } } return false; } @Override public boolean canContinueToUse() { return !this.hasEaten || this.ticks < MAX_TICKS; } @Override public void start() { super.start(); Main.LOGGER.info("Eating crops"); this.ticks = 0; this.hasEaten = false; } @Override public void stop() { this.hasEaten = true; } @Override public void tick() { ++this.ticks; List<BlockPos> cropsToEat = Utils.getSurroundingBlockPoses(this.mob).stream() .filter(blockPos -> Utils.blockPosIsCrop(this.level, blockPos)).toList(); if (this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { for (BlockPos cropToEat : cropsToEat) { this.level.destroyBlock(cropToEat, false); this.mob.ate(); this.mob.gameEvent(GameEvent.EAT, this.mob.eyeBlockPosition()); } if (cropsToEat.size() > 0) { this.hasEaten = true; } } } } registerGoals: @Override protected void registerGoals() { this.goalSelector.addGoal(0, new FloatGoal(this)); this.goalSelector.addGoal(15, new WaterAvoidingRandomStrollGoal(this, 1.0, 0f)); this.goalSelector.addGoal(1, new HurtByTargetGoal(this)); this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1d, false)); this.goalSelector.addGoal(2, new MoveToCropGoal(this)); this.goalSelector.addGoal(2, new EatCropGoal(this)); }
February 22, 20223 yr Author I took a look at MoveToBlockGoal's canUse method and noticed that it has a nextStartTick that it uses in determining if it can be used. I'll try playing with that to see if it makes a difference. Edited February 22, 20223 yr by yeetsche420 spelling
February 22, 20223 yr before anything else, EatCropGoal should have higher priority than MoveToCropGoal. if both could theoretically execute, main goal should execute.
February 23, 20223 yr Author I managed to get something I like. Here's my code: MoveToCropGoal: public class MoveToCropGoal extends MoveToBlockGoal { private static final int GIVE_UP_TICKS = 600; private boolean shouldStop; private boolean isNearCrop; private int timesRun; public MoveToCropGoal(PathfinderMob mob) { super(mob, 1.0, 100, 20); timesRun = 0; } @Override protected boolean isValidTarget(LevelReader levelReader, BlockPos blockPos) { return levelReader.getBlockState(blockPos).getBlock() instanceof CropBlock; } @Override public boolean shouldRecalculatePath() { return this.tryTicks % 10 == 0; } @Override public void start() { super.start(); shouldStop = false; isNearCrop = true; ++timesRun; } @Override protected int nextStartTick(PathfinderMob pathfinderMob) { return (isNearCrop || timesRun == 0) ? 3 : super.nextStartTick(pathfinderMob); } @Override public void stop() { shouldStop = true; } @Override public boolean isInterruptable() { return false; } @Override protected boolean isReachedTarget() { return super.isReachedTarget() || Utils.getSurroundingBlockPoses(mob).stream().anyMatch(blockPos -> Utils.blockPosIsCrop(mob.level, blockPos)); } @Override public boolean canContinueToUse() { boolean canContinue = !shouldStop && !this.isReachedTarget() && tryTicks <= GIVE_UP_TICKS && isValidTarget(mob.level, blockPos); if (!canContinue) { isNearCrop = findNearestBlock(); } return canContinue; } } EatCropGoal: public class EatCropGoal extends EatBlockGoal { protected final Mob mob; protected final Level level; private final int MAX_TICKS = 10; private int ticks; private boolean hasEaten; public EatCropGoal(Mob mob) { super(mob); this.mob = mob; this.level = mob.level; } @Override public boolean canUse() { return Utils.getSurroundingBlockPoses(this.mob).stream() .anyMatch(blockPos -> Utils.blockPosIsCrop(this.level, blockPos)); } @Override public boolean canContinueToUse() { return !this.hasEaten && this.ticks < MAX_TICKS; } @Override public void start() { super.start(); this.ticks = 0; this.hasEaten = false; } @Override public boolean isInterruptable() { return false; } @Override public void stop() { this.hasEaten = true; } @Override public void tick() { ++this.ticks; if (this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { List<BlockPos> cropsToEat = Utils.getSurroundingBlockPoses(this.mob).stream() .filter(blockPos -> Utils.blockPosIsCrop(this.level, blockPos)).toList(); for (BlockPos cropToEat : cropsToEat) { this.level.destroyBlock(cropToEat, false); this.mob.ate(); this.mob.gameEvent(GameEvent.EAT, this.mob.eyeBlockPosition()); } if (cropsToEat.size() > 0) { this.hasEaten = true; } } } }
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.