Skip to content

Commit

Permalink
Split ball state into multiple classes
Browse files Browse the repository at this point in the history
  • Loading branch information
haykam821 committed Jul 9, 2024
1 parent 835e3b7 commit 6acd1d2
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.github.haykam821.volleyball.game.ball;

import io.github.haykam821.volleyball.game.phase.VolleyballActivePhase;
import io.github.haykam821.volleyball.game.player.PlayerEntry;
import io.github.haykam821.volleyball.game.player.team.TeamEntry;
import net.minecraft.entity.Entity;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;

public class ActiveBallState extends EntityBallState {
private static final Text INACTIVE_BALL_RESET_MESSAGE = Text.translatable("text.volleyball.inactive_ball_reset").formatted(Formatting.RED);

/**
* The number of ticks since the ball was last hit.
*/
private int ticksSinceHit = 0;

/**
* The team that last hit the ball.
*/
private TeamEntry possessionTeam;

public ActiveBallState(VolleyballActivePhase phase, Entity ball, TeamEntry possessionTeam) {
super(phase, ball);

this.possessionTeam = possessionTeam;
}

@Override
public void onTick() {
if (this.onEntityTick()) return;

if (this.ticksSinceHit >= this.phase.getConfig().getInactiveBallTicks()) {
this.phase.resetBall();
this.phase.getGameSpace().getPlayers().sendMessage(INACTIVE_BALL_RESET_MESSAGE);
} else {
this.ticksSinceHit += 1;

if (this.possessionTeam != null && this.phase.hasBallLandedOffCourt(this.ball)) {
this.possessionTeam.getOtherTeam().incrementScore();
}
}
}

public void onHitBall(PlayerEntry entry) {
this.ticksSinceHit = 0;

if (entry != null) {
this.possessionTeam = entry.getTeam();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.github.haykam821.volleyball.game.ball;

import java.util.Objects;

import io.github.haykam821.volleyball.game.phase.VolleyballActivePhase;
import io.github.haykam821.volleyball.game.player.PlayerEntry;
import net.minecraft.entity.Entity;

public abstract class BallState {
protected final VolleyballActivePhase phase;

public BallState(VolleyballActivePhase phase) {
this.phase = Objects.requireNonNull(phase);
}

public abstract void onTick();

public boolean onAttackEntity(PlayerEntry entry, Entity entity) {
return false;
}

public void destroy(BallState newState) {
return;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.github.haykam821.volleyball.game.ball;

import java.util.Objects;

import io.github.haykam821.volleyball.game.phase.VolleyballActivePhase;
import io.github.haykam821.volleyball.game.player.PlayerEntry;
import io.github.haykam821.volleyball.game.player.team.TeamEntry;
import net.minecraft.entity.Entity;

public abstract class EntityBallState extends BallState {
protected final Entity ball;

public EntityBallState(VolleyballActivePhase phase, Entity ball) {
super(phase);

this.ball = Objects.requireNonNull(ball);
}

/**
* @return whether the ball tick was handled by shared logic
*/
protected final boolean onEntityTick() {
if (!this.ball.isAlive()) {
this.phase.resetBall();
return true;
}

for (TeamEntry team : this.phase.getTeams()) {
if (team.isBallOnCourt(this.ball)) {
team.getOtherTeam().incrementScore();
return true;
}
}

return false;
}

@Override
public final boolean onAttackEntity(PlayerEntry entry, Entity entity) {
if (entity == this.ball) {
this.onHitBall(entry);
return true;
}

return false;
}

protected abstract void onHitBall(PlayerEntry entry);

@Override
public final void destroy(BallState newState) {
if (!(newState instanceof EntityBallState)) {
this.ball.discard();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.github.haykam821.volleyball.game.ball;

import io.github.haykam821.volleyball.game.phase.VolleyballActivePhase;

public class InactiveBallState extends BallState {
private int ticksUntilSpawn;

public InactiveBallState(VolleyballActivePhase phase) {
super(phase);

this.ticksUntilSpawn = phase.getConfig().getResetBallTicks();
}

@Override
public void onTick() {
this.ticksUntilSpawn -= 1;

if (this.ticksUntilSpawn <= 0) {
this.phase.spawnBall();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.haykam821.volleyball.game.ball;

import io.github.haykam821.volleyball.game.phase.VolleyballActivePhase;
import io.github.haykam821.volleyball.game.player.PlayerEntry;
import io.github.haykam821.volleyball.game.player.team.TeamEntry;
import net.minecraft.entity.Entity;

public class ReadyBallState extends EntityBallState {
public ReadyBallState(VolleyballActivePhase phase, Entity ball) {
super(phase, ball);
}

@Override
public void onTick() {
this.onEntityTick();
}

@Override
public void onHitBall(PlayerEntry entry) {
TeamEntry possessionTeam = entry == null ? null : entry.getTeam();
this.phase.setBallState(new ActiveBallState(this.phase, this.ball, possessionTeam));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import java.util.Set;

import io.github.haykam821.volleyball.game.VolleyballConfig;
import io.github.haykam821.volleyball.game.ball.BallState;
import io.github.haykam821.volleyball.game.ball.InactiveBallState;
import io.github.haykam821.volleyball.game.ball.ReadyBallState;
import io.github.haykam821.volleyball.game.map.VolleyballMap;
import io.github.haykam821.volleyball.game.player.PlayerEntry;
import io.github.haykam821.volleyball.game.player.WinManager;
Expand All @@ -20,7 +23,6 @@
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.world.GameMode;
Expand Down Expand Up @@ -53,16 +55,8 @@ public class VolleyballActivePhase implements PlayerAttackEntityEvent, GameActiv
private final Set<TeamEntry> teams;
private final WinManager winManager = new WinManager(this);
private final VolleyballScoreboard scoreboard;
private Entity ball;
private int ballTicks = 0;
/**
* The number of ticks since the ball was last hit.
*/
private int inactiveBallTicks = -1;
/**
* The team that last hit the ball.
*/
private TeamEntry possessionTeam;

private BallState ballState;

public VolleyballActivePhase(ServerWorld world, GameSpace gameSpace, VolleyballMap map, TeamManager teamManager, GlobalWidgets widgets, VolleyballConfig config, Text shortName) {
this.world = world;
Expand Down Expand Up @@ -143,17 +137,12 @@ public static void open(GameSpace gameSpace, ServerWorld world, VolleyballMap ma
// Listeners
@Override
public ActionResult onAttackEntity(ServerPlayerEntity attacker, Hand hand, Entity attacked, EntityHitResult hitResult) {
if (attacked == this.ball) {
PlayerEntry entry = this.getPlayerEntry(attacker);

if (entry != null) {
this.possessionTeam = entry.getTeam();
}

this.inactiveBallTicks = 0;
PlayerEntry entry = this.getPlayerEntry(attacker);

if (this.ballState.onAttackEntity(entry, attacked)) {
return ActionResult.PASS;
}

return ActionResult.FAIL;
}

Expand All @@ -163,6 +152,8 @@ public void onEnable() {
player.spawn();
player.clearInventory();
}

this.spawnBall();
}

@Override
Expand All @@ -171,30 +162,7 @@ public void onTick() {
entry.onTick();
}

if (this.ball == null || !this.ball.isAlive()) {
this.ballTicks -= 1;
if (this.ballTicks <= 0) {
this.spawnBall();
}
} else if (this.inactiveBallTicks >= this.config.getInactiveBallTicks()) {
this.resetBall();
this.gameSpace.getPlayers().sendMessage(this.getInactiveBallResetText());
} else {
if (this.inactiveBallTicks >= 0) {
this.inactiveBallTicks += 1;
}

for (TeamEntry team : this.getTeams()) {
if (team.isBallOnCourt(this.ball)) {
team.getOtherTeam().incrementScore();
break;
}
}

if (this.possessionTeam != null && this.hasBallLandedOffCourt()) {
this.possessionTeam.getOtherTeam().incrementScore();
}
}
this.ballState.onTick();

// Attempt to determine a winner
if (this.winManager.checkForWinner()) {
Expand Down Expand Up @@ -268,32 +236,34 @@ public TeamEntry getChatTeam(ServerPlayerEntity sender) {
return entry.getTeam();
}

public Entity spawnBall() {
this.ball = this.config.getBallEntityConfig().createEntity(this.world, this.world.getRandom());
this.inactiveBallTicks = -1;
this.possessionTeam = null;

this.map.spawnAtBall(this.world, this.ball);
this.world.spawnEntity(this.ball);
/**
* Spawns a ball, transitioning it into the ready state.
*/
public void spawnBall() {
Entity ball = this.config.getBallEntityConfig().createEntity(this.world, this.world.getRandom());
this.setBallState(new ReadyBallState(this, ball));

return this.ball;
this.map.spawnAtBall(this.world, ball);
this.world.spawnEntity(ball);
}

/**
* Resets the ball, transitioning it into the ready state.
*/
public void resetBall() {
if (this.ball != null) {
this.ball.discard();
this.ball = null;
}

this.ballTicks = this.config.getResetBallTicks();
this.setBallState(new InactiveBallState(this));
}

private boolean hasBallLandedOffCourt() {
return this.ball != null && this.ball.isOnGround() && !this.map.getBallSpawnBox().intersects(ball.getBoundingBox());
public void setBallState(BallState ballState) {
if (this.ballState != null) {
this.ballState.destroy(ballState);
}

this.ballState = ballState;
}

public Text getInactiveBallResetText() {
return Text.translatable("text.volleyball.inactive_ball_reset").formatted(Formatting.RED);
public boolean hasBallLandedOffCourt(Entity ball) {
return ball.isOnGround() && !this.map.getBallSpawnBox().intersects(ball.getBoundingBox());
}

public void pling() {
Expand Down

0 comments on commit 6acd1d2

Please sign in to comment.