diff --git a/src/main/java/eu/mhsl/minecraft/endlessjumper/EndlessJumper.java b/src/main/java/eu/mhsl/minecraft/endlessjumper/EndlessJumper.java index fae6833..83a0a7d 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/EndlessJumper.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/EndlessJumper.java @@ -3,6 +3,8 @@ package eu.mhsl.minecraft.endlessjumper; import eu.mhsl.minecraft.endlessjumper.generator.JumpGenerator; import net.minestom.server.Auth; import net.minestom.server.MinecraftServer; +import net.minestom.server.command.builder.Command; +import net.minestom.server.coordinate.Vec; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.Player; import net.minestom.server.entity.PlayerSkin; @@ -20,7 +22,6 @@ import net.minestom.server.event.player.PlayerUseItemEvent; import net.minestom.server.event.player.PlayerUseItemOnBlockEvent; import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceContainer; -import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; @@ -32,26 +33,20 @@ import java.util.concurrent.ConcurrentHashMap; public class EndlessJumper { private static final Pos START = new Pos(0, 100, 0); + private static final Pos SPAWN = START.add(0.5, 1, 0.5); private static final Map games = new ConcurrentHashMap<>(); public static void main(String[] args) throws IOException { MinecraftServer server = MinecraftServer.init(new Auth.Velocity(readForwardingSecret())); Progress.load(); - InstanceManager instanceManager = MinecraftServer.getInstanceManager(); GlobalEventHandler eventHandler = MinecraftServer.getGlobalEventHandler(); PlayerSkin skin = PlayerSkin.fromUsername("Notch"); eventHandler.addListener(AsyncPlayerConfigurationEvent.class, event -> { final Player p = event.getPlayer(); - p.setRespawnPoint(START.add(0.5, 1.5, 0.5)); - - final InstanceContainer world = instanceManager.createInstanceContainer(); - world.setBlock(START, Block.DIAMOND_BLOCK); - // Tageszeit steuert der jeweilige Abschnitt - world.defaultClock().pause(); - + p.setRespawnPoint(SPAWN); p.setSkin(skin); - event.setSpawningInstance(world); + event.setSpawningInstance(createWorld()); }); eventHandler.addListener(PlayerSpawnEvent.class, event -> { @@ -94,13 +89,46 @@ public class EndlessJumper { games.remove(p); ScoreTab.leave(p); // jede Partie hat eine eigene Welt: nach dem Verlassen freigeben - Instance world = p.getInstance(); - if(world != null) MinecraftServer.getSchedulerManager().scheduleNextTick(() -> instanceManager.unregisterInstance(world)); + releaseLater(p.getInstance()); }); + Command reset = new Command("reset"); + reset.setDefaultExecutor((sender, context) -> { + if(sender instanceof Player player) resetGame(player); + }); + MinecraftServer.getCommandManager().register(reset); + server.start("0.0.0.0", 25501); } + private static InstanceContainer createWorld() { + InstanceContainer world = MinecraftServer.getInstanceManager().createInstanceContainer(); + world.setBlock(START, Block.DIAMOND_BLOCK); + // Tageszeit steuert der jeweilige Abschnitt + world.defaultClock().pause(); + return world; + } + + private static void releaseLater(Instance world) { + if(world != null) MinecraftServer.getSchedulerManager().scheduleNextTick(() -> MinecraftServer.getInstanceManager().unregisterInstance(world)); + } + + /** /reset: Fortschritt, Strecke, Score und Joker zurücksetzen und in einer frischen Welt bei 0 beginnen. */ + private static void resetGame(Player player) { + JumpGenerator old = games.remove(player); + if(old != null) old.close(); + Progress.reset(player.getUuid()); + + Instance oldWorld = player.getInstance(); + InstanceContainer world = createWorld(); + player.setVelocity(Vec.ZERO); + player.setInstance(world, SPAWN).thenRun(() -> { + Jokers.giveStart(player); + games.put(player, new JumpGenerator(START, 0, player, world)); + releaseLater(oldWorld); + }); + } + private static void useJoker(Player player, ItemStack item) { JumpGenerator generator = games.get(player); if(generator != null && Jokers.BRIDGE.matches(item) && generator.useBridge()) Jokers.BRIDGE.take(player); diff --git a/src/main/java/eu/mhsl/minecraft/endlessjumper/Progress.java b/src/main/java/eu/mhsl/minecraft/endlessjumper/Progress.java index 3097310..3ef5024 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/Progress.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/Progress.java @@ -40,6 +40,11 @@ public class Progress { writer.execute(Progress::write); } + public static void reset(UUID player) { + checkpoints.remove(player.toString()); + writer.execute(Progress::write); + } + /** Erst in eine Temp-Datei, dann atomar umbenennen: ein Absturz hinterlässt nie eine halbe Datei. */ private static void write() { try { diff --git a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockPalette.java b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockPalette.java index f63f141..707b96a 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockPalette.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockPalette.java @@ -75,6 +75,11 @@ public class BlockPalette { return STAGES.getFirst(); } + /** Der letzte Abschnitt geht endlos weiter. */ + public static boolean isEndless(Stage stage) { + return stage == STAGES.getLast(); + } + public static Block getRandom(int index, Random random) { List blocks = stageAt(index).blocks(); return blocks.get(random.nextInt(blocks.size())); diff --git a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java index 1e27e48..2c61583 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java @@ -53,6 +53,7 @@ public class JumpGenerator { private static final TextColor COMBO_COLOR = TextColor.color(255, 69, 0); private static final Title CHECKPOINT_TITLE = Title.title(Component.empty(), Component.text("Checkpoint", NamedTextColor.GREEN)); + private static final Component ENDLESS_HINT = Component.text(" · /reset für einen Neustart", NamedTextColor.DARK_GRAY); private static final Title FINALE_TITLE = Title.title(Component.text("Geschafft!", NamedTextColor.GOLD), Component.text("Du hast das End erreicht", NamedTextColor.YELLOW)); private static final Sound CHECKPOINT_SOUND = Sound.sound(SoundEvent.ENTITY_PLAYER_LEVELUP, Sound.Source.BLOCK, 0.3f, 1f); private static final Sound STAGE_SOUND = Sound.sound(SoundEvent.UI_TOAST_CHALLENGE_COMPLETE, Sound.Source.MASTER, 0.4f, 1f); @@ -154,13 +155,14 @@ public class JumpGenerator { // Abschnitt dauerhaft in der Actionbar (Titel werden von der Combo-Anzeige überschrieben) BlockPalette.Stage stage = BlockPalette.stageAt(index); - if(stage != BlockPalette.stageAt(previous)) { + boolean newStage = stage != BlockPalette.stageAt(previous); + if(newStage) { this.atmosphere.enter(stage); this.player.playSound(STAGE_SOUND); - this.player.sendActionBar(Component.text(stage.name(), NamedTextColor.GOLD)); - } else { - this.player.sendActionBar(Component.text(stage.name(), NamedTextColor.GRAY)); } + Component stageText = Component.text(stage.name(), newStage ? NamedTextColor.GOLD : NamedTextColor.GRAY); + if(BlockPalette.isEndless(stage)) stageText = stageText.append(ENDLESS_HINT); + this.player.sendActionBar(stageText); if(previous < FINALE_AT && index >= FINALE_AT) this.celebrate(); @@ -348,6 +350,11 @@ public class JumpGenerator { })).delay(TaskSchedule.seconds(8)).schedule(); } + /** Beendet alles, was an den Spieler gebunden weiterläuft (Partikel, Musik). */ + public void close() { + this.atmosphere.stop(); + } + /** Streckenblöcke und ihr Licht sind geschützt, alles andere (Brücke, Rettung) darf abgebaut werden. */ public boolean isBreakable(Point pos) { return !this.world.getBlock(pos).compare(Block.LIGHT) && !this.courseIndex.containsKey(BlockKey.of(pos)); diff --git a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/StageAtmosphere.java b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/StageAtmosphere.java index 0d9f8e7..d0c0c2d 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/StageAtmosphere.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/StageAtmosphere.java @@ -23,6 +23,7 @@ class StageAtmosphere { private final Instance world; private BlockPalette.Stage stage; private Task timeTransition; + private final Task ambient; StageAtmosphere(Player player, Instance world, BlockPalette.Stage initial) { this.player = player; @@ -32,7 +33,13 @@ class StageAtmosphere { this.world.refreshTime(); this.playMusic(); // an den Spieler gebunden, endet also mit ihm - player.scheduler().buildTask(this::spawnAmbient).repeat(TaskSchedule.tick(4)).schedule(); + this.ambient = player.scheduler().buildTask(this::spawnAmbient).repeat(TaskSchedule.tick(4)).schedule(); + } + + void stop() { + this.ambient.cancel(); + if(this.timeTransition != null) this.timeTransition.cancel(); + this.player.stopSound(SoundStop.source(Sound.Source.MUSIC)); } void enter(BlockPalette.Stage next) {