Refactor phase transitions and add particle effects for flags

Simplified phase transition logic by removing unnecessary parameters and added proper executor shutdown at the end of the game. Enhanced visuals by introducing particle effects around flags when a team wins. Also implemented a method to skip the current game phase.
This commit is contained in:
Teriuihi 2025-02-11 21:41:33 +01:00
parent 66c24d852d
commit aad63f174e
3 changed files with 50 additions and 13 deletions

View File

@ -96,12 +96,40 @@ public class Flag implements Runnable {
Bukkit.getScheduler().runTask(main, () -> flagLocation.getBlock().setType(flagItem.getType()));
}
private void spawnFlagParticleRing() {
Location center = flagLocation.clone();
World world = center.getWorld();
double radius = 0.7;
double gap = 0.2;
double circumference = 2 * Math.PI * radius;
int particleCount = (int) (circumference / gap);
Particle particle = Particle.DUST;
TeamColor color = winningTeam.getColor();
// Generate particle positions
for (double heightOffset = 0; heightOffset < 2; heightOffset += 0.5) {
center.setY(center.getY() + 0.5);
for (int i = 0; i < particleCount; i++) {
double angle = 2 * Math.PI * i / particleCount;
double x = center.getX() + radius * Math.cos(angle);
double z = center.getZ() + radius * Math.sin(angle);
double y = center.getY();
world.spawnParticle(particle, x, y, z, 1, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(color.r(), color.g(), color.b()), 1));
}
}
}
@Override
public void run() {
if (flagCarrier != null) {
checkFlagCarrier();
return;
}
if (winningTeam != null) {
spawnFlagParticleRing();
return;
}
if (flagLocation == null) {
log.warn("Tried to run Flag without a flag location, spawn it first");
return;

View File

@ -77,7 +77,7 @@ public class GameManager {
executorService.shutdown();
executorService = Executors.newSingleThreadScheduledExecutor();
}
runningGame = new RunningGame(this, duration, flag);
runningGame = new RunningGame(this, duration, flag, executorService);
executorService.scheduleAtFixedRate(runningGame, 0, 1, TimeUnit.SECONDS);
}

View File

@ -13,6 +13,7 @@ import javax.annotation.Nullable;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.concurrent.ScheduledExecutorService;
@Slf4j
public class RunningGame implements Runnable {
@ -20,14 +21,16 @@ public class RunningGame implements Runnable {
private final HashMap<GamePhase, Duration> phaseDurations = GameConfig.PHASES.getGAME_PHASE_DURATION();
private final GameManager gameManager;
private final Flag flag;
private final ScheduledExecutorService executorService;
@Getter
private GamePhase currentPhase = GamePhase.values()[0];
private Instant phaseStartTime = null;
private int lastMinuteBroadcast = 0;
public RunningGame(GameManager gameManager, Duration gameDuration, Flag flag) {
public RunningGame(GameManager gameManager, Duration gameDuration, Flag flag, ScheduledExecutorService executorService) {
this.gameManager = gameManager;
this.flag = flag;
this.executorService = executorService;
phaseDurations.put(GamePhase.COMBAT, gameDuration);
}
@ -37,16 +40,16 @@ public class RunningGame implements Runnable {
GamePhase nextPhase = (currentPhase.ordinal() + 1 < GamePhase.values().length) ? GamePhase.values()[currentPhase.ordinal() + 1] : null;
if (phaseStartTime == null) {
phaseStartTime = Instant.now();
nextPhaseActions(null, currentPhase, nextPhase);
nextPhaseActions(null, currentPhase);
}
if (Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
GamePhase previousPhase = currentPhase;
currentPhase = GamePhase.values()[currentPhase.ordinal() + 1]; //TODO fix this running out of bounds
nextPhaseActions(previousPhase, currentPhase, nextPhase);
if (nextPhase != null && Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
nextPhaseActions(currentPhase, nextPhase);
phaseStartTime = Instant.now();
} else if (nextPhase != null) {
broadcastNextPhaseStartTime(currentPhase, nextPhase);
} else {
executorService.shutdown();
}
} catch (Exception e) {
log.error("Unexpected error in running game", e);
@ -54,15 +57,13 @@ public class RunningGame implements Runnable {
}
}
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase, @Nullable GamePhase nextPhase) {
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase) {
//TODO command to go to next phase
this.currentPhase = phase;
if (previousPhase != null) {
gameManager.getPhaseExecutor(previousPhase).end(phase);
}
gameManager.getPhaseExecutor(phase).start(flag);
if (nextPhase != null) {
broadcastNextPhaseStartTime(phase, nextPhase);
}
}
private void broadcastNextPhaseStartTime(GamePhase currentPhase, GamePhase nextPhase) {//TODO check how this works/what it should do
@ -86,9 +87,17 @@ public class RunningGame implements Runnable {
}
}
public void skipCurrentPhase() {
GamePhase nextPhase = (currentPhase.ordinal() + 1 < GamePhase.values().length) ? GamePhase.values()[currentPhase.ordinal() + 1] : null;
if (nextPhase == null) {
log.warn("Tried to skip phase {} but there is no next phase", currentPhase);
return;
}
nextPhaseActions(currentPhase, nextPhase);
}
public void end() {
//TODO say the phase ended early?
currentPhase = GamePhase.ENDED;
nextPhaseActions(null, currentPhase, null);
nextPhaseActions(currentPhase, GamePhase.ENDED);
}
}