Skip to content

Commit

Permalink
Implement birdhouse bird spawning [OLB-51] (#6)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jamestjw authored Mar 14, 2022
1 parent 9147114 commit ba69a83
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ runClient.launch
runData.launch
runServer.launch

src/generated
src/generated
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
Expand Down
6 changes: 0 additions & 6 deletions config/checkstyle/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,6 @@
</module>

<module name="InvalidJavadocPosition"/>

<module name="MissingJavadocMethodCheck">
<property name="minLineCount" value="1"/>
<property name="allowMissingPropertyJavadoc" value="true"/>
<property name="ignoreMethodNamesRegex" value="(set.*|get.*)"/>
</module>
</module>

<property name='localeCountry' value='' />
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/com/ocelotslovebirds/birdhaus/HelloWorld.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -26,7 +33,11 @@ public class BirdhouseBlockEntity extends BlockEntity {
private final ItemStackHandler itemHandler = createHandler();
private final LazyOptional<IItemHandler> 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.
Expand Down Expand Up @@ -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
Expand All @@ -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
);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/ocelotslovebirds/birdhaus/ticker/Ticker.java
Original file line number Diff line number Diff line change
@@ -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();
}
13 changes: 0 additions & 13 deletions src/test/java/com/ocelotslovebirds/birdhaus/HelloWorldTest.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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());
}
}
}

0 comments on commit ba69a83

Please sign in to comment.