From 0faff2bd0c5a82ff584eecf282ac0ef045bb017e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Wed, 7 Oct 2026 23:49:49 +0200 Subject: [PATCH] added difficulty curve and themed stages from overworld to the end - jumps start easy (2-3 blocks, rarely sideways/upwards) and get harder over the first 1000 jumps up to the previous difficulty (3-4 blocks, often sideways/upwards) - block themes follow a full playthrough: meadow, forest, underground, caves, deepslate, nether portal, nether, fortress & bastion, stronghold, portal room, the end (from 1000) - title and sound when a new stage is reached - gold/diamond checkpoint markers unchanged Co-Authored-By: Claude Opus 5.5 --- .../endlessjumper/generator/BlockPalette.java | 81 ++++++++++++++++--- .../generator/JumpGenerator.java | 39 ++++++++- 2 files changed, 107 insertions(+), 13 deletions(-) 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 7750bb8..1af51b5 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockPalette.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockPalette.java @@ -2,21 +2,80 @@ package eu.mhsl.minecraft.endlessjumper.generator; import net.minestom.server.instance.block.Block; -import java.util.ArrayList; +import java.util.List; import java.util.Random; +/** + * Block-Themen entlang des Fortschritts: einmal durch das komplette Spiel, + * von der Wiese über Untergrund, Nether und Stronghold bis ins End (ab Sprung 1000). + */ public class BlockPalette { - public static Block getRandom(int seed) { - Random r = new Random(seed); - ArrayList blocks = new ArrayList<>(); + public record Stage(int from, String name, List blocks) { + } - blocks.add(Block.COPPER_BLOCK); - blocks.add(Block.CUT_COPPER); - blocks.add(Block.WEATHERED_COPPER); - blocks.add(Block.WEATHERED_CUT_COPPER); - blocks.add(Block.OXIDIZED_COPPER); - blocks.add(Block.OXIDIZED_CUT_COPPER); + public static final List STAGES = List.of( + new Stage(0, "Wiese", List.of( + Block.GRASS_BLOCK, Block.GRASS_BLOCK, Block.MOSS_BLOCK, Block.PODZOL, Block.ROOTED_DIRT + )), + new Stage(75, "Wald", List.of( + Block.OAK_LOG, Block.BIRCH_LOG, Block.SPRUCE_LOG, Block.CHERRY_LOG, + Block.OAK_LEAVES, Block.BIRCH_LEAVES, Block.SPRUCE_LEAVES, Block.CHERRY_LEAVES + )), + new Stage(150, "Untergrund", List.of( + Block.DIRT, Block.COARSE_DIRT, Block.STONE, Block.STONE, Block.ANDESITE, + Block.GRANITE, Block.DIORITE, Block.COBBLESTONE + )), + new Stage(250, "Höhlen", List.of( + Block.STONE, Block.COAL_ORE, Block.IRON_ORE, Block.COPPER_ORE, + Block.GOLD_ORE, Block.REDSTONE_ORE, Block.LAPIS_ORE + )), + new Stage(350, "Tiefenschiefer", List.of( + Block.DEEPSLATE, Block.DEEPSLATE, Block.TUFF, Block.DEEPSLATE_DIAMOND_ORE, + Block.DEEPSLATE_IRON_ORE, Block.AMETHYST_BLOCK + )), + new Stage(450, "Netherportal", List.of( + Block.OBSIDIAN, Block.OBSIDIAN, Block.CRYING_OBSIDIAN + )), + new Stage(475, "Nether", List.of( + Block.NETHERRACK, Block.NETHERRACK, Block.CRIMSON_NYLIUM, Block.WARPED_NYLIUM, + Block.SOUL_SOIL, Block.GLOWSTONE, Block.NETHER_QUARTZ_ORE, Block.NETHER_GOLD_ORE + )), + new Stage(625, "Festung & Bastion", List.of( + Block.NETHER_BRICKS, Block.RED_NETHER_BRICKS, Block.BLACKSTONE, + Block.POLISHED_BLACKSTONE_BRICKS, Block.GILDED_BLACKSTONE, Block.BASALT + )), + new Stage(750, "Stronghold", List.of( + Block.STONE_BRICKS, Block.MOSSY_STONE_BRICKS, Block.CRACKED_STONE_BRICKS, + Block.CHISELED_STONE_BRICKS, Block.BOOKSHELF + )), + new Stage(925, "Portalraum", List.of( + Block.END_PORTAL_FRAME, Block.END_PORTAL_FRAME.withProperty("eye", "true"), + Block.STONE_BRICKS, Block.MOSSY_STONE_BRICKS + )), + new Stage(1000, "End", List.of( + Block.END_STONE, Block.END_STONE, Block.END_STONE_BRICKS, + Block.PURPUR_BLOCK, Block.PURPUR_PILLAR, Block.OBSIDIAN + )) + ); - return blocks.get(r.nextInt(0, blocks.size())); + public static Stage stageAt(int index) { + Stage current = STAGES.getFirst(); + for(Stage stage : STAGES) { + if(index >= stage.from()) current = stage; + } + return current; + } + + /** Liefert die Stufe, die genau bei diesem Index beginnt, sonst null. */ + public static Stage stageStartingAt(int index) { + for(Stage stage : STAGES) { + if(stage.from() == index) return stage; + } + return null; + } + + 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 c9074e5..f76dd4d 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java @@ -26,6 +26,8 @@ public class JumpGenerator { private final int preview = 2; + private static final int FULL_DIFFICULTY_AT = 1000; + public JumpGenerator(Pos starting, Player player, InstanceContainer world) { this.player = player; @@ -45,8 +47,9 @@ public class JumpGenerator { this.player.setLevel((this.blocks.size()-this.preview-1)); Random r = new Random(); - Pos target = blocks.get(blocks.size()-1).add(r.nextInt(-1, 2), r.nextInt(this.player.getPosition().y() < 80 ? 0 : -1, this.player.getPosition().y() > 120 ? 1 : 2), r.nextInt(3, 5)); - this.world.setBlock(target, isHundredth ? Block.DIAMOND_BLOCK : isTenth ? Block.GOLD_BLOCK : BlockPalette.getRandom((this.blocks.size() / 10))); + int index = this.blocks.size(); + Pos target = blocks.get(blocks.size()-1).add(this.nextOffset(index, r)); + this.world.setBlock(target, isHundredth ? Block.DIAMOND_BLOCK : isTenth ? Block.GOLD_BLOCK : BlockPalette.getRandom(index, r)); if(standingTenth) { Pos current = this.blocks.get(this.blocks.size()-this.preview-1); @@ -55,6 +58,12 @@ public class JumpGenerator { this.player.showTitle(Title.title(Component.text(""), Component.text("Checkpoint", TextColor.color(0, 255, 0)))); this.player.sendPacket(new ParticlePacket(Particle.CRIT, current, current.add(0.5), 1.5f, 50)); } + + BlockPalette.Stage reachedStage = BlockPalette.stageStartingAt(this.blocks.size()-this.preview-1); + if(reachedStage != null && reachedStage.from() > 0) { + this.player.playSound(Sound.sound(SoundEvent.UI_TOAST_CHALLENGE_COMPLETE, Sound.Source.MASTER, 0.4f, 1f)); + this.player.showTitle(Title.title(Component.text(reachedStage.name(), TextColor.color(255, 170, 0)), Component.text(""))); + } this.world.setBlock(target.add(0, 1 ,0), Block.LIGHT); this.blocks.add(target); this.player.sendPacket(new ParticlePacket(Particle.CRIT, target, target.add(0.5), 1, 10)); @@ -62,6 +71,32 @@ public class JumpGenerator { this.combo++; } + /** + * Abstand zum nächsten Block. Die Schwierigkeit steigt über die ersten {@link #FULL_DIFFICULTY_AT} + * Sprünge von leicht (2–3 weit, kaum seitlich/aufwärts) bis zum bisherigen Niveau (3–4 weit, + * oft seitlich und aufwärts) und bleibt danach dort. + */ + private Pos nextOffset(int index, Random r) { + double difficulty = Math.min(1.0, (double) index / FULL_DIFFICULTY_AT); + + int dz = 2; + if(r.nextDouble() < 0.5 + 0.5 * difficulty) dz++; + if(r.nextDouble() < 0.5 * difficulty) dz++; + + int dx = r.nextDouble() < 0.1 + 0.57 * difficulty ? (r.nextBoolean() ? 1 : -1) : 0; + + double y = this.player.getPosition().y(); + double roll = r.nextDouble(); + int dy = 0; + if(roll < 0.05 + 0.28 * difficulty && y <= 120) dy = 1; + else if(roll > 1 - (0.2 + 0.13 * difficulty) && y >= 80) dy = -1; + + // weite Diagonalsprünge nach oben erst gegen Ende + if(dy == 1 && dz == 4 && dx != 0 && difficulty < 0.7) dx = 0; + + return new Pos(dx, dy, dz); + } + public Pos getNextLanding() { return this.blocks.get(this.blocks.size()-(preview+1)); }