Skip to content

Commit

Permalink
Prevent game from ending more than once and end messages
Browse files Browse the repository at this point in the history
  • Loading branch information
ellieisjelly committed Jan 5, 2024
1 parent 892fd19 commit 5c22839
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 84 deletions.
69 changes: 10 additions & 59 deletions src/main/java/me/ellieis/Sabotage/game/config/SabotageConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<SabotageConfig> 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;
}
}
81 changes: 58 additions & 23 deletions src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 -> {
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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());
Expand All @@ -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());
}
}
Expand Down Expand Up @@ -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));
}
}
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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 {
Expand All @@ -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;
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ public static GameOpenProcedure Open(GameOpenContext<SabotageConfig> 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);
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/data/sabotage/games/sabotage.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/data/sabotage/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 5c22839

Please sign in to comment.