From ba69a83430d87b603e346907cbe32ad5022e8e09 Mon Sep 17 00:00:00 2001 From: James Tan Juan Whei Date: Sun, 13 Mar 2022 22:44:05 -0400 Subject: [PATCH] Implement birdhouse bird spawning [OLB-51] (#6) * Implement birdhouse bird spawning - Birdhouse spawns birds every 50 ticks - Implement ticker to manage server ticks * Spawn bird at random offset from birdhouses * Refactor seed consumption to use the Ticker class * Fix seed consumption behaviour * Set bird spawn y-offset to 10 units above a birdhouse --- .gitignore | 2 +- build.gradle | 3 + config/checkstyle/checkstyle.xml | 6 -- .../ocelotslovebirds/birdhaus/HelloWorld.java | 7 -- .../birdhaus/blocks/BirdhouseBlockEntity.java | 75 +++++++++++++++---- .../birdhaus/ticker/FixedIntervalTicker.java | 25 +++++++ .../birdhaus/ticker/Ticker.java | 11 +++ .../birdhaus/HelloWorldTest.java | 13 ---- .../ticker/FixedIntervalTickerTest.java | 55 ++++++++++++++ 9 files changed, 157 insertions(+), 40 deletions(-) delete mode 100644 src/main/java/com/ocelotslovebirds/birdhaus/HelloWorld.java create mode 100644 src/main/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTicker.java create mode 100644 src/main/java/com/ocelotslovebirds/birdhaus/ticker/Ticker.java delete mode 100644 src/test/java/com/ocelotslovebirds/birdhaus/HelloWorldTest.java create mode 100644 src/test/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTickerTest.java diff --git a/.gitignore b/.gitignore index 7555dcb..f5cebb1 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,4 @@ runClient.launch runData.launch runServer.launch -src/generated \ No newline at end of file +src/generated diff --git a/build.gradle b/build.gradle index a413144..4b2e1b9 100644 --- a/build.gradle +++ b/build.gradle @@ -4,11 +4,13 @@ buildscript { // These repositories are only for Gradle plugins, put any other repositories in the repository block further below maven { url = 'https://maven.minecraftforge.net' } maven { url = 'https://maven.parchmentmc.org' } + maven { url = 'https://plugins.gradle.org/m2/' } mavenCentral() } dependencies { classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true classpath 'org.parchmentmc:librarian:1.+' + classpath 'com.adarshr:gradle-test-logger-plugin:3.2.0' } } apply plugin: 'net.minecraftforge.gradle' @@ -18,6 +20,7 @@ apply plugin: 'eclipse' apply plugin: 'maven-publish' apply plugin: 'java' apply plugin: 'checkstyle' +apply plugin: 'com.adarshr.test-logger' version = '1.18-0.1.1' group = 'com.ocelotslovebirds.birdhaus' // http://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 3119a68..b64ee5b 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -391,12 +391,6 @@ - - - - - - diff --git a/src/main/java/com/ocelotslovebirds/birdhaus/HelloWorld.java b/src/main/java/com/ocelotslovebirds/birdhaus/HelloWorld.java deleted file mode 100644 index f6f58f4..0000000 --- a/src/main/java/com/ocelotslovebirds/birdhaus/HelloWorld.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ocelotslovebirds.birdhaus; - -public class HelloWorld { - public static int getFourOneTwo() { - return 412; - } -} diff --git a/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java b/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java index 96ff1a4..028aa5b 100644 --- a/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java +++ b/src/main/java/com/ocelotslovebirds/birdhaus/blocks/BirdhouseBlockEntity.java @@ -1,10 +1,17 @@ package com.ocelotslovebirds.birdhaus.blocks; +import java.util.concurrent.ThreadLocalRandom; + import com.ocelotslovebirds.birdhaus.setup.Registration; +import com.ocelotslovebirds.birdhaus.ticker.FixedIntervalTicker; +import com.ocelotslovebirds.birdhaus.ticker.Ticker; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +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; @@ -26,7 +33,11 @@ public class BirdhouseBlockEntity extends BlockEntity { private final ItemStackHandler itemHandler = createHandler(); private final LazyOptional handler = LazyOptional.of(() -> itemHandler); - private int counter; + private Ticker tickerForBirdSpawns = new FixedIntervalTicker(150); + private Ticker tickerForSeedConsumption = new FixedIntervalTicker(100); + + // The block is active if it able to consume seeds + private Boolean isActive = false; /** * @param pos Position of the block. @@ -59,8 +70,6 @@ public void load(CompoundTag tag) { } - // TODO: Hook in bird spawning into the Entity ticker. - /** * 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 @@ -70,22 +79,62 @@ public void load(CompoundTag tag) { */ public void tickServer() { - if (counter > 0) { - counter--; - setChanged(); + handleSeedsForTick(); + handleBirdSpawnForTick(); + } + + /* + * @param xOffset x offset (East-West axis) at which to spawn the bird + * @param yOffset y offset (Up-Down axis) at which to spawn the bird + * @param yOffset z offset (North-South axis) at which to spawn the bird + * All offsets are relative to the birdhouse + */ + private void spawnNewBird(int xOffset, int yOffset, int zOffset) { + // Dirty type casting because it works + ServerLevel lvl = (ServerLevel) this.getLevel(); + Parrot newBird = new Parrot(EntityType.PARROT, lvl); + lvl.addFreshEntity(newBird); + BlockPos desBlockPos = this.getBlockPos().offset(xOffset, yOffset, zOffset); + + // Move the spawned bird to the destination + // 0.0 because it works, not sure what this supposed to be + newBird.moveTo(desBlockPos, (float) 0.0, (float) 0.0); + } + + private void handleBirdSpawnForTick() { + if (tickerForBirdSpawns.tick()) { + // Random x offset + int xOffset = ThreadLocalRandom.current().nextInt(-20, 20); + // Spawn bird 10 units higher than the birdhouse + int yOffset = this.getBlockPos().getY() + 10; + // Random z offset + int zOffset = ThreadLocalRandom.current().nextInt(-20, 20); + spawnNewBird(xOffset, yOffset, zOffset); } - if (counter <= 0) { + } + + private void handleSeedsForTick() { + if (isActive) { + // If the birdhouse is active, try consuming a seed if (!itemHandler.extractItem(0, 1, false).isEmpty()) { - counter = 100; - setChanged(); + // 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; } } - // When adding seeds, if the seeds are "burnable" then set the block to active. + // Update the block state if there was a change BlockState blockState = getBlockState(); - if (blockState.getValue(BlockStateProperties.CONDITIONAL) != counter > 0) { - level.setBlock(worldPosition, blockState.setValue(BlockStateProperties.CONDITIONAL, counter > 0), - Block.UPDATE_ALL); + if (blockState.getValue(BlockStateProperties.CONDITIONAL) != isActive) { + level.setBlock( + worldPosition, + blockState.setValue(BlockStateProperties.CONDITIONAL, isActive), + Block.UPDATE_ALL + ); } } diff --git a/src/main/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTicker.java b/src/main/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTicker.java new file mode 100644 index 0000000..e4dde26 --- /dev/null +++ b/src/main/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTicker.java @@ -0,0 +1,25 @@ +package com.ocelotslovebirds.birdhaus.ticker; + +public class FixedIntervalTicker implements Ticker { + private final int interval; + private int counter; + + /** + * @param interval How often the ticker should tick + */ + public FixedIntervalTicker(int interval) { + this.interval = interval; + this.counter = 0; + } + + public boolean tick() { + this.counter++; + + if (this.counter == this.interval) { + this.counter = 0; + return true; + } + + return false; + } +} diff --git a/src/main/java/com/ocelotslovebirds/birdhaus/ticker/Ticker.java b/src/main/java/com/ocelotslovebirds/birdhaus/ticker/Ticker.java new file mode 100644 index 0000000..1dfea25 --- /dev/null +++ b/src/main/java/com/ocelotslovebirds/birdhaus/ticker/Ticker.java @@ -0,0 +1,11 @@ +package com.ocelotslovebirds.birdhaus.ticker; + +// A ticker class helps keep track of when events should happen +public interface Ticker { + /* + This function should be called for every tick that passes. It returns + true when a particular tick matches the ticker's pattern. Patterns can + be things like every 10 ticks or every other tick. + */ + public boolean tick(); +} diff --git a/src/test/java/com/ocelotslovebirds/birdhaus/HelloWorldTest.java b/src/test/java/com/ocelotslovebirds/birdhaus/HelloWorldTest.java deleted file mode 100644 index 9116f63..0000000 --- a/src/test/java/com/ocelotslovebirds/birdhaus/HelloWorldTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.ocelotslovebirds.birdhaus; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - - -public class HelloWorldTest { - @Test - public void testGetFourOneTwo() { - assertEquals(412, HelloWorld.getFourOneTwo()); - } -} diff --git a/src/test/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTickerTest.java b/src/test/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTickerTest.java new file mode 100644 index 0000000..c062ed9 --- /dev/null +++ b/src/test/java/com/ocelotslovebirds/birdhaus/ticker/FixedIntervalTickerTest.java @@ -0,0 +1,55 @@ +package com.ocelotslovebirds.birdhaus.ticker; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + + +public class FixedIntervalTickerTest { + @Test + public void alwaysTicks() { + Ticker ticker = new FixedIntervalTicker(1); + + // We assume that if it works 10 times in a row, + // it will continue to always work + for (int i = 0; i < 10; i++) { + assertTrue(ticker.tick()); + } + } + + @Test + public void ticksEveryOtherTime() { + Ticker ticker = new FixedIntervalTicker(2); + + // We assume that if it works 5 times in a row, + // it will continue to always work + for (int i = 0; i < 5; i++) { + assertFalse(ticker.tick()); + assertTrue(ticker.tick()); + } + } + + @Test + public void ticksEveryTenTicks() { + Ticker ticker = new FixedIntervalTicker(10); + + // We assume that if it works 5 times in a row, + // it will continue to always work + for (int i = 0; i < 5; i++) { + // Returns false 9 times + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + assertFalse(ticker.tick()); + + // Returns true once + assertTrue(ticker.tick()); + } + } +}