From 5c22839eb3033fdebea18d0de6da5aa32c78e583 Mon Sep 17 00:00:00 2001 From: ellieisjelly Date: Fri, 5 Jan 2024 18:51:20 -0300 Subject: [PATCH] Prevent game from ending more than once and end messages --- .../Sabotage/game/config/SabotageConfig.java | 69 +++------------- .../Sabotage/game/phase/SabotageActive.java | 81 +++++++++++++------ .../Sabotage/game/phase/SabotageWaiting.java | 4 +- .../data/sabotage/games/sabotage.json | 1 + .../resources/data/sabotage/lang/en_us.json | 4 + 5 files changed, 75 insertions(+), 84 deletions(-) diff --git a/src/main/java/me/ellieis/Sabotage/game/config/SabotageConfig.java b/src/main/java/me/ellieis/Sabotage/game/config/SabotageConfig.java index e77e087..4f27eb9 100644 --- a/src/main/java/me/ellieis/Sabotage/game/config/SabotageConfig.java +++ b/src/main/java/me/ellieis/Sabotage/game/config/SabotageConfig.java @@ -5,67 +5,18 @@ import net.minecraft.util.Identifier; import xyz.nucleoid.plasmid.game.common.config.PlayerConfig; -public class SabotageConfig { - private final Identifier map; - private final int countdownTime; - private final int gracePeriod; - private final int timeLimit; - private final InnocentConfig innocentConfig; - private final DetectiveConfig detectiveConfig; - private final SaboteurConfig saboteurConfig; - private final PlayerConfig playerConfig; +public record SabotageConfig(Identifier map, int countdownTime, int gracePeriod, int timeLimit, int endDelay, InnocentConfig innocentConfig, DetectiveConfig detectiveConfig, SaboteurConfig saboteurConfig, PlayerConfig playerConfig) { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Identifier.CODEC.fieldOf("map").forGetter(SabotageConfig::getMap), - Codec.INT.fieldOf("countdown_time").forGetter(SabotageConfig::getCountdownTime), - Codec.INT.fieldOf("grace_period").forGetter(SabotageConfig::getGracePeriod), - Codec.INT.fieldOf("time_limit").forGetter(SabotageConfig::getTimeLimit), - InnocentConfig.CODEC.fieldOf("innocent").forGetter(SabotageConfig::getInnocentConfig), - DetectiveConfig.CODEC.fieldOf("detective").forGetter(SabotageConfig::getDetectiveConfig), - SaboteurConfig.CODEC.fieldOf("saboteur").forGetter(SabotageConfig::getSaboteurConfig), - PlayerConfig.CODEC.fieldOf("players").forGetter(SabotageConfig::getPlayerConfig) + Identifier.CODEC.fieldOf("map").forGetter(SabotageConfig::map), + Codec.INT.fieldOf("countdown_time").forGetter(SabotageConfig::countdownTime), + Codec.INT.fieldOf("grace_period").forGetter(SabotageConfig::gracePeriod), + Codec.INT.fieldOf("time_limit").forGetter(SabotageConfig::timeLimit), + Codec.INT.fieldOf("end_delay").forGetter(SabotageConfig::endDelay), + InnocentConfig.CODEC.fieldOf("innocent").forGetter(SabotageConfig::innocentConfig), + DetectiveConfig.CODEC.fieldOf("detective").forGetter(SabotageConfig::detectiveConfig), + SaboteurConfig.CODEC.fieldOf("saboteur").forGetter(SabotageConfig::saboteurConfig), + PlayerConfig.CODEC.fieldOf("players").forGetter(SabotageConfig::playerConfig) ).apply(instance, SabotageConfig::new) ); - public SabotageConfig(Identifier map, int countdownTime, int gracePeriod, int timeLimit, InnocentConfig innocentConfig, DetectiveConfig detectiveConfig, SaboteurConfig saboteurConfig, PlayerConfig playerConfig) { - this.map = map; - this.countdownTime = countdownTime; - this.gracePeriod = gracePeriod; - this.timeLimit = timeLimit; - this.innocentConfig = innocentConfig; - this.detectiveConfig = detectiveConfig; - this.saboteurConfig = saboteurConfig; - this.playerConfig = playerConfig; - } - - public int getCountdownTime() { - return countdownTime; - } - - public int getGracePeriod() { - return gracePeriod; - } - - public int getTimeLimit() { - return timeLimit; - } - - public InnocentConfig getInnocentConfig() { - return this.innocentConfig; - } - - public DetectiveConfig getDetectiveConfig() { - return this.detectiveConfig; - } - - public SaboteurConfig getSaboteurConfig() { - return saboteurConfig; - } - - public PlayerConfig getPlayerConfig() { - return this.playerConfig; - } - - public Identifier getMap() { - return this.map; - } } diff --git a/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java b/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java index 77bee70..5d49200 100644 --- a/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java +++ b/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java @@ -57,8 +57,11 @@ public class SabotageActive { private final MutablePlayerSet saboteurs; private final MutablePlayerSet detectives; private final MutablePlayerSet innocents; + // used in the game end message to list all saboteurs + private PlayerSet initialSaboteurs; private final KarmaManager karmaManager; private long startTime; + private long endTime; private GameActivity activity; private GameStates gameState = GameStates.COUNTDOWN; private GlobalWidgets widgets; @@ -102,8 +105,10 @@ private static void rules(GameActivity activity) { private static String getPlayerNamesInSet(PlayerSet plrs) { String result = ""; for (ServerPlayerEntity plr : plrs) { - result = result + plr.getName() + ", "; + result = result + plr.getName().getString() + ", "; } + // get rid of the last comma + result = result.substring(0, result.length() - 2); return result; } @@ -117,7 +122,7 @@ private PlayerSet getAlivePlayers() { return plrs; } public void updateSidebars() { - long timeLeft = (long) Math.abs(Math.floor((world.getTime() / 20) - (startTime / 20)) - config.getCountdownTime() - config.getGracePeriod() - config.getTimeLimit()); + long timeLeft = (long) Math.abs(Math.floor((world.getTime() / 20) - (startTime / 20)) - config.countdownTime() - config.gracePeriod() - config.timeLimit()); long minutes = timeLeft / 60; String seconds = Long.toString(timeLeft % 60); if (seconds.length() == 1) { @@ -203,6 +208,7 @@ public void pickRoles() { innocents.add(plr); } } + initialSaboteurs = saboteurs.copy(gameSpace.getServer()); innocents.showTitle(Text.translatable("sabotage.role_reveal", Text.translatable("sabotage.innocent").formatted(Formatting.GREEN)), 10, 80, 10); innocents.playSound(SoundEvents.ENTITY_ILLUSIONER_CAST_SPELL); detectives.showTitle(Text.translatable("sabotage.role_reveal", Text.translatable("sabotage.detective").formatted(Formatting.DARK_BLUE)), 10, 80, 10); @@ -255,8 +261,29 @@ public void Start() { } public void End(EndReason endReason) { + if (endReason == EndReason.NONE) return; + if (gameState == GameStates.ENDED) return; + PlayerSet plrs = gameSpace.getPlayers(); + endTime = world.getTime(); gameState = GameStates.ENDED; rules(activity); + plrs.sendMessage(Text.translatable("sabotage.game_end", Text.literal(getPlayerNamesInSet(initialSaboteurs)).formatted(Formatting.RED))); + if (endReason == EndReason.INNOCENT_WIN) { + plrs.sendMessage(Text.translatable( + "sabotage.game_end.innocents", + Text.translatable("sabotage.innocents").formatted(Formatting.GREEN), + Text.translatable("sabotage.detectives").formatted(Formatting.DARK_BLUE), + Text.translatable("sabotage.saboteurs").formatted(Formatting.RED) + )); + } else if (endReason == EndReason.SABOTEUR_WIN) { + plrs.sendMessage(Text.translatable( + "sabotage.game_end.saboteurs", + Text.translatable("sabotage.saboteurs").formatted(Formatting.RED), + Text.translatable("sabotage.innocents").formatted(Formatting.GREEN) + )); + } else if (endReason == EndReason.TIMEOUT) { + plrs.sendMessage(Text.translatable("sabotage.game_end.none")); + } } public static void Open(GameSpace gameSpace, ServerWorld world, SabotageMap map, SabotageConfig config) { gameSpace.setActivity(activity -> { @@ -276,7 +303,7 @@ public static void Open(GameSpace gameSpace, ServerWorld world, SabotageMap map, activity.listen(GamePlayerEvents.OFFER, game::onOffer); PlayerSet plrs = game.gameSpace.getPlayers(); - plrs.showTitle(Text.literal(Integer.toString(game.config.getCountdownTime())).formatted(Formatting.GOLD), 20); + plrs.showTitle(Text.literal(Integer.toString(game.config.countdownTime())).formatted(Formatting.GOLD), 20); plrs.playSound(SoundEvents.BLOCK_NOTE_BLOCK_HARP.value(), SoundCategory.PLAYERS, 1.0F, 2.0F); plrs.forEach(plr -> { game.map.spawnEntity(world, plr); @@ -298,6 +325,9 @@ private boolean onChat(ServerPlayerEntity plr, SignedMessage signedMessage, Mess } private ActionResult onDeath(ServerPlayerEntity plr, DamageSource damageSource) { + if (gameState == GameStates.ENDED) { + return ActionResult.FAIL; + } Entity entityAttacker = damageSource.getAttacker(); Roles plrRole = getPlayerRole(plr); plr.changeGameMode(GameMode.SPECTATOR); @@ -306,7 +336,7 @@ private ActionResult onDeath(ServerPlayerEntity plr, DamageSource damageSource) // surely there's a better way to do this.. switch(attackerRole) { case SABOTEUR -> { - SaboteurConfig config = this.config.getSaboteurConfig(); + SaboteurConfig config = this.config.saboteurConfig(); switch(plrRole) { case INNOCENT -> { karmaManager.incrementKarma(attacker, config.innocentKarmaAward()); @@ -326,12 +356,12 @@ private ActionResult onDeath(ServerPlayerEntity plr, DamageSource damageSource) } case DETECTIVE -> { - DetectiveConfig config = this.config.getDetectiveConfig(); + DetectiveConfig config = this.config.detectiveConfig(); awardPlayerKill(attacker, plr, plrRole, config.innocentKarmaPenalty(), config.detectiveKarmaPenalty(), config.saboteurKarmaAward()); } case INNOCENT -> { - InnocentConfig config = this.config.getInnocentConfig(); + InnocentConfig config = this.config.innocentConfig(); awardPlayerKill(attacker, plr, plrRole, config.innocentKarmaPenalty(), config.detectiveKarmaPenalty(), config.saboteurKarmaAward()); } } @@ -376,14 +406,15 @@ private void onPlayerRemove(ServerPlayerEntity plr) { } else if (role == Roles.INNOCENT) { innocents.remove(plr); } - - if (role != Roles.NONE) { - EndReason endReason = checkWinCondition(); - if (endReason != EndReason.NONE) { - End(endReason); - } else { - PlayerSet plrs = getAlivePlayers(); - plrs.sendMessage(Text.translatable("sabotage.kill_message", plr.getName(), plrs.size() - 1).formatted(Formatting.YELLOW)); + if (gameState != GameStates.ENDED) { + if (role != Roles.NONE) { + EndReason endReason = checkWinCondition(); + if (endReason != EndReason.NONE) { + End(endReason); + } else { + PlayerSet plrs = getAlivePlayers(); + plrs.sendMessage(Text.translatable("sabotage.kill_message", plr.getName(), plrs.size() - 1).formatted(Formatting.YELLOW)); + } } } } @@ -416,11 +447,11 @@ public void onTick() { // second has passed PlayerSet plrs = gameSpace.getPlayers(); int secondsSinceStart = (int) Math.floor((time / 20) - (startTime / 20)); - int countdownTime = config.getCountdownTime(); + int countdownTime = config.countdownTime(); if (secondsSinceStart >= countdownTime) { gameState = GameStates.GRACE_PERIOD; plrs.playSound(SoundEvents.BLOCK_RESPAWN_ANCHOR_CHARGE); - plrs.sendMessage(Text.translatable("sabotage.game_start", config.getGracePeriod()).formatted(Formatting.YELLOW)); + plrs.sendMessage(Text.translatable("sabotage.game_start", config.gracePeriod()).formatted(Formatting.YELLOW)); } else { plrs.showTitle(Text.literal(Integer.toString(countdownTime - secondsSinceStart)).formatted(Formatting.GOLD), 20); plrs.playSound(SoundEvents.BLOCK_NOTE_BLOCK_HARP.value(), SoundCategory.PLAYERS, 1.0F, 2.0F); @@ -437,8 +468,8 @@ public void onTick() { } case GRACE_PERIOD -> { - int secondsSinceStart = (int) Math.floor((time / 20) - (startTime / 20)) - config.getCountdownTime(); - int gracePeriod = config.getGracePeriod(); + int secondsSinceStart = (int) Math.floor((time / 20) - (startTime / 20)) - config.countdownTime(); + int gracePeriod = config.gracePeriod(); if (secondsSinceStart >= gracePeriod) { Start(); } else { @@ -448,11 +479,10 @@ public void onTick() { } case ACTIVE -> { - // to-do: implement game loop if (time % 20 == 0) { // second has passed - double timePassed = Math.floor((world.getTime() / 20) - (startTime / 20)) - config.getCountdownTime() - config.getGracePeriod(); - int timeLimit = config.getTimeLimit(); + double timePassed = Math.floor((world.getTime() / 20) - (startTime / 20)) - config.countdownTime() - config.gracePeriod(); + int timeLimit = config.timeLimit(); if (timePassed >= timeLimit) { End(EndReason.TIMEOUT); return; @@ -466,8 +496,13 @@ public void onTick() { } case ENDED -> { - // to-do: countdown before ending - gameSpace.close(GameCloseReason.FINISHED); + if (time % 20 == 0) { + // second has passed + double timePassed = world.getTime() / 20 - endTime / 20; + if (timePassed >= config.endDelay()) { + gameSpace.close(GameCloseReason.FINISHED); + } + } } default -> { // unknown state, close game. diff --git a/src/main/java/me/ellieis/Sabotage/game/phase/SabotageWaiting.java b/src/main/java/me/ellieis/Sabotage/game/phase/SabotageWaiting.java index 5805db1..735bd9f 100644 --- a/src/main/java/me/ellieis/Sabotage/game/phase/SabotageWaiting.java +++ b/src/main/java/me/ellieis/Sabotage/game/phase/SabotageWaiting.java @@ -53,14 +53,14 @@ public static GameOpenProcedure Open(GameOpenContext context) { SabotageConfig config = context.game().config(); MinecraftServer server = context.server(); // set up how the world that this minigame will take place in should be constructed - SabotageMap map = SabotageMapBuilder.build(server, config.getMap()); + SabotageMap map = SabotageMapBuilder.build(server, config.map()); RuntimeWorldConfig worldConfig = new RuntimeWorldConfig() .setGenerator(map.asChunkGenerator(server)) .setTimeOfDay(6000); return context.openWithWorld(worldConfig, (activity, world) -> { SabotageWaiting game = new SabotageWaiting(config, activity.getGameSpace(), map, world); - GameWaitingLobby.addTo(activity, config.getPlayerConfig()); + GameWaitingLobby.addTo(activity, config.playerConfig()); rules(activity); activity.listen(GamePlayerEvents.OFFER, game::onOffer); diff --git a/src/main/resources/data/sabotage/games/sabotage.json b/src/main/resources/data/sabotage/games/sabotage.json index 7db69c1..e577a15 100644 --- a/src/main/resources/data/sabotage/games/sabotage.json +++ b/src/main/resources/data/sabotage/games/sabotage.json @@ -4,6 +4,7 @@ "countdown_time": 5, "grace_period": 15, "time_limit": 100, + "end_delay": 10, "innocent": { "innocent_karma_penalty": 20, "detective_karma_penalty": 100, diff --git a/src/main/resources/data/sabotage/lang/en_us.json b/src/main/resources/data/sabotage/lang/en_us.json index 7c898f2..a677e8c 100644 --- a/src/main/resources/data/sabotage/lang/en_us.json +++ b/src/main/resources/data/sabotage/lang/en_us.json @@ -9,6 +9,10 @@ "sabotage.sidebar.role.desc": "Find and kill all %s", "sabotage.sidebar.time_left": "Time left: %02d:%02d", "sabotage.game_start": "The game has started! You have %s seconds to collect items, gear, or hide. Your roles will be selected after the grace period.", + "sabotage.game_end": "The game has ended! The following players were saboteurs: %s", + "sabotage.game_end.innocents": "Congratulations to all alive %s and %s for figuring out who the %s were.", + "sabotage.game_end.saboteurs": "Congratulations to all alive %s for eradicating all %s", + "sabotage.game_end.none": "No one has won.", "sabotage.role_reveal": "You are a %s", "sabotage.innocent": "Innocent", "sabotage.innocents": "Innocents",