diff --git a/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java b/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java index 028aa5b..4b56511 100644 --- a/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java +++ b/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java @@ -1,7 +1,9 @@ package com.ocelotslovebirds.birdhaus.blocks; +import java.util.List; import java.util.concurrent.ThreadLocalRandom; +import com.ocelotslovebirds.birdhaus.mobai.HangAroundBirdhouseGoal; import com.ocelotslovebirds.birdhaus.setup.Registration; import com.ocelotslovebirds.birdhaus.ticker.FixedIntervalTicker; import com.ocelotslovebirds.birdhaus.ticker.Ticker; @@ -11,12 +13,15 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.ai.goal.WrappedGoal; import net.minecraft.world.entity.animal.Parrot; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.phys.AABB; import net.minecraftforge.common.Tags; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; @@ -35,10 +40,18 @@ public class BirdhouseBlockEntity extends BlockEntity { private Ticker tickerForBirdSpawns = new FixedIntervalTicker(150); private Ticker tickerForSeedConsumption = new FixedIntervalTicker(100); + private Ticker tickerForBirdhouseGoalModification = new FixedIntervalTicker(100); // The block is active if it able to consume seeds private Boolean isActive = false; - + private AABB birdHouseBB = new AABB( + this.getBlockPos().getX() - 10, + this.getBlockPos().getY(), + this.getBlockPos().getZ() - 10, + this.getBlockPos().getX() + 10, + this.getBlockPos().getY() + 10, + this.getBlockPos().getZ() + 10 + ); /** * @param pos Position of the block. * @param state State of the block being created. @@ -69,18 +82,16 @@ public void load(CompoundTag tag) { super.load(tag); } - /** - * This is the main loop for the birdhouse. It needs to be improved to allow for the spawning of birds however at - * the moment it works well for - * 1. Extracting seeds from the inventory stack, - * 2. Reacting to seeds being placed in the stack. - * 3. Creates a ticker that can be used to time bird spawns eventually. + * This is the main loop for the birdhouse. + * It ticks all the events that should be happening + * after a certain interval. */ public void tickServer() { handleSeedsForTick(); handleBirdSpawnForTick(); + handleParrotGoalForTick(); } /* @@ -110,29 +121,84 @@ private void handleBirdSpawnForTick() { // Random z offset int zOffset = ThreadLocalRandom.current().nextInt(-20, 20); spawnNewBird(xOffset, yOffset, zOffset); + + } + } + + private void handleParrotGoalForTick() { + if (tickerForBirdhouseGoalModification.tick()) { + if (this.isActive) { + applyBirdHouseGoalToSurroundingParrots(); + } else { + removeBirdHouseGoalToSurroundingParrots(); + } + } + } + + /** + * Applies the "HangingAroundBirdhouseGoal" to birds in the + * birdhouse bounding box. + * + * TODO: Make this threadsafe. + */ + private void applyBirdHouseGoalToSurroundingParrots() { + List parrots = this.level.getEntitiesOfClass(Parrot.class, this.birdHouseBB); + for (Parrot temp:parrots) { + // Check to see if they already have the goal applied. If not, apply it. + if (!temp.goalSelector.getAvailableGoals().stream() + .anyMatch(wg -> wg.getGoal() instanceof HangAroundBirdhouseGoal)) { + temp.goalSelector.addGoal(0, new HangAroundBirdhouseGoal(temp, 1.0, 60, false, this.getBlockPos())); + } + } + } + + /** + * Removes the "HangingAroundBirdhouseGoal" to birds in the + * birdhouse bounding box. + * + * TODO: Make this threadsafe. + */ + private void removeBirdHouseGoalToSurroundingParrots() { + List parrots = this.level.getEntitiesOfClass(Parrot.class, this.birdHouseBB); + for (Parrot temp:parrots) { + // Check to see if they already have the goal applied. If not, apply it. + if (temp.goalSelector.getAvailableGoals().stream() + .anyMatch(wg -> wg.getGoal() instanceof HangAroundBirdhouseGoal)) { + Goal toRemove = null; + for (WrappedGoal g : temp.goalSelector.getAvailableGoals()) { + if (g.getGoal() instanceof HangAroundBirdhouseGoal) { + toRemove = g.getGoal(); + } + } + if (toRemove != null) { + temp.goalSelector.removeGoal(toRemove); + } + } } } private void handleSeedsForTick() { - if (isActive) { - // If the birdhouse is active, try consuming a seed + // Tick the birdhouse to check it needs to consume a seed + if (tickerForSeedConsumption.tick()) { + // If a seed was successfully extracted from the birdhouse if (!itemHandler.extractItem(0, 1, false).isEmpty()) { - // If a seed was successfully consumed, set birdhouse to inactive - isActive = false; - } - } else { - // If birdhouse is inactive, start ticking to eventually reactivate it - if (tickerForSeedConsumption.tick()) { - isActive = true; + // Check if the state of the BHouse is not active + if (!this.isActive) { + // If not active, set to active + this.isActive = true; + } + } else { // If nothing was extracted from the BHouse (inventory was empty) + // Set BHouse state to false + this.isActive = false; } } // Update the block state if there was a change BlockState blockState = getBlockState(); - if (blockState.getValue(BlockStateProperties.CONDITIONAL) != isActive) { + if (blockState.getValue(BlockStateProperties.CONDITIONAL) != this.isActive) { level.setBlock( worldPosition, - blockState.setValue(BlockStateProperties.CONDITIONAL, isActive), + blockState.setValue(BlockStateProperties.CONDITIONAL, this.isActive), Block.UPDATE_ALL ); } diff --git a/src/main/java/com/ocelotslovebirds/birdhaus/mobai/HangAroundBirdhouseGoal.java b/src/main/java/com/ocelotslovebirds/birdhaus/mobai/HangAroundBirdhouseGoal.java new file mode 100644 index 0000000..13e1d8b --- /dev/null +++ b/src/main/java/com/ocelotslovebirds/birdhaus/mobai/HangAroundBirdhouseGoal.java @@ -0,0 +1,41 @@ +package com.ocelotslovebirds.birdhaus.mobai; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.goal.RandomStrollGoal; +import net.minecraft.world.phys.Vec3; + + +public class HangAroundBirdhouseGoal extends RandomStrollGoal { + + private Vec3 bHousePosVec; + + /** + * Constructor for the parrot goal. Makes parrots stay + * around a birdhouse when it has seeds in its internal inventory. + * @param pMob the mob getting this goal + * @param pSpeedModifier modifies mob move speed + * @param pInterval interval to check if the goal should be sought + * @param pCheckNoActionTime boolean to check to see if the mob has not been performing an action + * @param bHousePos position of the birdhouse passing the goal to the mob + */ + public HangAroundBirdhouseGoal(PathfinderMob pMob, double pSpeedModifier, + int pInterval, boolean pCheckNoActionTime, BlockPos bHousePos) { + super(pMob, pSpeedModifier, pInterval, pCheckNoActionTime); + this.bHousePosVec = new Vec3(bHousePos.getX(), bHousePos.getY(), bHousePos.getZ()); + } + + /** + * Returns the position for the parrot to move to. This will be the position of the birdhouse that gave it the goal. + * If for some reason, the bird is left with this goal with no birdhouse in sight, it will default to behaving like + * "RandomWanderGoal". + */ + @Override + protected Vec3 getPosition() { + if (this.bHousePosVec != null) { + return this.bHousePosVec; + } else { + return super.getPosition(); + } + } +}