Implement round progression system with countdown, stages, and /hg start command
This commit is contained in:
parent
ab6112b9da
commit
c832a6648b
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.alttd.hunger_games.commands.subcommands;
|
||||||
|
|
||||||
|
import com.alttd.hunger_games.commands.SubCommand;
|
||||||
|
import com.alttd.hunger_games.config.Messages;
|
||||||
|
import com.alttd.hunger_games.services.Round;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class StartRound extends SubCommand {
|
||||||
|
|
||||||
|
private final Round round;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||||
|
round.startRound();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "start";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpMessage() {
|
||||||
|
return Messages.HELP.START_ROUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
package com.alttd.hunger_games.config;
|
package com.alttd.hunger_games.config;
|
||||||
|
|
||||||
|
import com.alttd.hunger_games.data_objects.GameStage;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Config extends AbstractConfig {
|
public class Config extends AbstractConfig {
|
||||||
|
|
@ -31,4 +37,60 @@ public class Config extends AbstractConfig {
|
||||||
private static void load() {
|
private static void load() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ROUND {
|
||||||
|
private static final String prefix = "round.";
|
||||||
|
|
||||||
|
public static Duration COUNTDOWN = Duration.ofSeconds(10);
|
||||||
|
public static List<GameStage> STAGES = List.of(
|
||||||
|
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(1000).build(),
|
||||||
|
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(750).build(),
|
||||||
|
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(500).build()
|
||||||
|
);
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static void load() {
|
||||||
|
int countdownSeconds = config.getInt(prefix, "countdown-seconds", Math.toIntExact(COUNTDOWN.toSeconds()));
|
||||||
|
COUNTDOWN = Duration.ofSeconds(countdownSeconds);
|
||||||
|
|
||||||
|
ConfigurationSection configurationSection = config.getConfigurationSection(prefix + "stages");
|
||||||
|
Set<String> keys = configurationSection.getKeys(false);
|
||||||
|
STAGES = getGameStages(keys, configurationSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<GameStage> getGameStages(Set<String> keys, ConfigurationSection configurationSection) {
|
||||||
|
if (keys.isEmpty()) {
|
||||||
|
int key = 0;
|
||||||
|
for (GameStage stage : STAGES) {
|
||||||
|
ConfigurationSection section = configurationSection.createSection(String.valueOf(key++));
|
||||||
|
section.set("duration-seconds", stage.getDuration().toSeconds());
|
||||||
|
section.set("world-border-size", stage.getWorldBorderSize());
|
||||||
|
}
|
||||||
|
return STAGES;
|
||||||
|
}
|
||||||
|
List<GameStage> gameStageList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String key : keys) {
|
||||||
|
ConfigurationSection section = configurationSection.getConfigurationSection(key);
|
||||||
|
if (section == null) {
|
||||||
|
throw new IllegalStateException("Stage section is null for key [" + key + "] but is in the list of retrieved keys");
|
||||||
|
}
|
||||||
|
if (!section.contains("duration-seconds")) {
|
||||||
|
log.error("Missing duration-seconds in stage {}.", key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!section.contains("world-border-size")) {
|
||||||
|
log.error("Missing world-border-size in stage {}.", key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int durationSeconds = section.getInt("duration-seconds");
|
||||||
|
int worldBorderSize = section.getInt("world-border-size");
|
||||||
|
gameStageList.add(GameStage.builder()
|
||||||
|
.duration(Duration.ofSeconds(durationSeconds))
|
||||||
|
.worldBorderSize(worldBorderSize)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return gameStageList;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ public class Messages extends AbstractConfig {
|
||||||
public static String HELP_MESSAGE = "<green>Show this menu: <gold>/hg help</gold></green>";
|
public static String HELP_MESSAGE = "<green>Show this menu: <gold>/hg help</gold></green>";
|
||||||
public static String ROUND_STATE = "<green>Show the current round state: <gold>/hg roundstate</gold></green>";
|
public static String ROUND_STATE = "<green>Show the current round state: <gold>/hg roundstate</gold></green>";
|
||||||
public static String REGISTER = "<green>Register a player for the game: <gold>/hg register <player></gold></green>";
|
public static String REGISTER = "<green>Register a player for the game: <gold>/hg register <player></gold></green>";
|
||||||
|
public static String START_ROUND = "<green>Start the game: <gold>/hg start</gold></green>";
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void load() {
|
private static void load() {
|
||||||
|
|
@ -35,6 +36,7 @@ public class Messages extends AbstractConfig {
|
||||||
HELP_MESSAGE = config.getString(prefix, "help", HELP_MESSAGE);
|
HELP_MESSAGE = config.getString(prefix, "help", HELP_MESSAGE);
|
||||||
ROUND_STATE = config.getString(prefix, "round-state", ROUND_STATE);
|
ROUND_STATE = config.getString(prefix, "round-state", ROUND_STATE);
|
||||||
REGISTER = config.getString(prefix, "register", REGISTER);
|
REGISTER = config.getString(prefix, "register", REGISTER);
|
||||||
|
START_ROUND = config.getString(prefix, "start", START_ROUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.alttd.hunger_games.data_objects;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Getter
|
||||||
|
public class GameStage {
|
||||||
|
|
||||||
|
private final Duration duration;
|
||||||
|
private final int worldBorderSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.alttd.hunger_games.game;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class GameStageHandler {
|
||||||
|
|
||||||
|
public static void handleStageChange(int worldBorderSize) {
|
||||||
|
//TODO change world border size and handle stage change
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleCountdownEnd() {
|
||||||
|
//TODO free players and handle stage change
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleWarmup() {
|
||||||
|
//TODO tp players to start area etc (might be handled by state change already, so maybe just messages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,11 +42,6 @@ public class PlayerService implements RoundListener {
|
||||||
unregisteredPlayers.forEach(player -> roundService.setPlayerState(player.getUniqueId(), PLAYER_STATE.SPECTATING));
|
unregisteredPlayers.forEach(player -> roundService.setPlayerState(player.getUniqueId(), PLAYER_STATE.SPECTATING));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void roundReset() {
|
|
||||||
//TODO: teleport everyone to spawn (or spectator?) location (from where they can join the game)
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<PLAYER_STATE> registerPlayer(Player player) {
|
public Optional<PLAYER_STATE> registerPlayer(Player player) {
|
||||||
if (roundState == null) {
|
if (roundState == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,24 @@
|
||||||
package com.alttd.hunger_games.services;
|
package com.alttd.hunger_games.services;
|
||||||
|
|
||||||
|
import com.alttd.hunger_games.config.Config;
|
||||||
|
import com.alttd.hunger_games.data_objects.GameStage;
|
||||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||||
|
import com.alttd.hunger_games.game.GameStageHandler;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class Round {
|
public class Round {
|
||||||
|
|
||||||
private static Round instance = null;
|
private static Round instance = null;
|
||||||
private final List<RoundListener> listeners = new ArrayList<>();
|
private final List<RoundListener> listeners = new ArrayList<>();
|
||||||
private ROUND_STATE roundState = null;
|
private ROUND_STATE roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||||
|
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
private Round() {
|
private Round() {
|
||||||
|
|
||||||
|
|
@ -33,24 +41,49 @@ public class Round {
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void stop() {
|
||||||
listeners.forEach(RoundListener::roundReset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void nextStage() {
|
private void nextStage() {
|
||||||
Optional<ROUND_STATE> optionalNextRoundState = roundState.next();
|
Optional<ROUND_STATE> optionalNextRoundState = roundState.next();
|
||||||
if (optionalNextRoundState.isEmpty()) {
|
if (optionalNextRoundState.isEmpty()) {
|
||||||
roundState = null;
|
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||||
reset();
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
roundState = optionalNextRoundState.get();
|
roundState = optionalNextRoundState.get();
|
||||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void startRound() {
|
||||||
|
if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||||
|
throw new IllegalStateException("Round can not be started before player registration.");
|
||||||
|
}
|
||||||
|
roundState = ROUND_STATE.COUNTDOWN;
|
||||||
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||||
|
GameStageHandler.handleWarmup();
|
||||||
|
Duration warmupDuration = Config.ROUND.COUNTDOWN;
|
||||||
|
scheduledExecutorService.schedule(() -> {
|
||||||
|
GameStageHandler.handleCountdownEnd();
|
||||||
|
nextStage();
|
||||||
|
scheduleNextStage(0);
|
||||||
|
}, warmupDuration.toSeconds(), TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleNextStage(int index) {
|
||||||
|
List<GameStage> gameStageList = Config.ROUND.STAGES;
|
||||||
|
if (gameStageList.size() <= index) {
|
||||||
|
nextStage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameStage gameStage = gameStageList.get(index);
|
||||||
|
Duration duration = gameStage.getDuration();
|
||||||
|
scheduledExecutorService.schedule(() -> {
|
||||||
|
GameStageHandler.handleStageChange(gameStage.getWorldBorderSize());
|
||||||
|
scheduleNextStage(index + 1);
|
||||||
|
}, duration.toSeconds(), TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,4 @@ public interface RoundListener {
|
||||||
|
|
||||||
void stateChange(ROUND_STATE roundState);
|
void stateChange(ROUND_STATE roundState);
|
||||||
|
|
||||||
void roundReset();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,8 @@ public class RoundService implements RoundListener {
|
||||||
@Override
|
@Override
|
||||||
public void stateChange(ROUND_STATE roundState) {
|
public void stateChange(ROUND_STATE roundState) {
|
||||||
this.roundState = roundState;
|
this.roundState = roundState;
|
||||||
}
|
if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||||
|
clear();
|
||||||
@Override
|
}
|
||||||
public void roundReset() {
|
|
||||||
clear();
|
|
||||||
roundState = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user