diff --git a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockTransition.java b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockTransition.java index f6f6915..893ee45 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockTransition.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/BlockTransition.java @@ -10,27 +10,35 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.timer.TaskSchedule; /** - * Lässt einen neuen Block als Block-Display aus dem vorherigen Block „herauswachsen“ und - * zu seiner Zielposition gleiten. Erst bei Ankunft wird der echte Block gesetzt. + * Lässt einen neuen Block als Block-Display klein oben aus dem vorherigen Block herauswachsen + * und zu seiner Zielposition gleiten. Erst bei Ankunft wird der echte Block gesetzt. */ public class BlockTransition { - private static final int DURATION_TICKS = 8; + public static final int DURATION_TICKS = 8; + /** Zeit vom Start bis der echte Block steht (Spawn-Vorlauf + Animation + Puffer). */ + public static final int TOTAL_TICKS = DURATION_TICKS + 3; private static final double START_SCALE = 0.3; - public static void animate(Instance world, Pos from, Pos to, Block block, Runnable onArrival) { + public static void animate(Instance world, Pos from, Pos to, Block block, int delayTicks, Runnable onArrival) { + world.scheduler().buildTask(() -> spawn(world, from, to, block, onArrival)) + .delay(TaskSchedule.tick(Math.max(1, delayTicks))) + .schedule(); + } + + private static void spawn(Instance world, Pos from, Pos to, Block block, Runnable onArrival) { Entity display = new Entity(EntityType.BLOCK_DISPLAY); display.setNoGravity(true); display.editEntityMeta(BlockDisplayMeta.class, meta -> { meta.setBlockState(block); meta.setPosRotInterpolationDuration(DURATION_TICKS); meta.setTransformationInterpolationDuration(DURATION_TICKS); - // klein und mittig im Ursprungsblock starten + // klein und mittig auf der Oberseite des vorherigen Blocks starten meta.setScale(new Vec(START_SCALE)); - meta.setTranslation(new Vec((1 - START_SCALE) / 2)); + meta.setTranslation(new Vec((1 - START_SCALE) / 2, 1, (1 - START_SCALE) / 2)); }); display.setInstance(world, from).thenRun(() -> { - // einen Moment warten, damit der Client das Display kennt, bevor die Interpolation startet + // kurz warten, damit der Client das Display kennt, bevor die Interpolation startet display.scheduler().buildTask(() -> { display.editEntityMeta(BlockDisplayMeta.class, meta -> { meta.setTransformationInterpolationStartDelta(0); @@ -43,7 +51,7 @@ public class BlockTransition { display.scheduler().buildTask(() -> { onArrival.run(); display.remove(); - }).delay(TaskSchedule.tick(DURATION_TICKS + 3)).schedule(); + }).delay(TaskSchedule.tick(TOTAL_TICKS)).schedule(); }); } } 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 2ff1b1a..13ae891 100644 --- a/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java +++ b/src/main/java/eu/mhsl/minecraft/endlessjumper/generator/JumpGenerator.java @@ -24,7 +24,7 @@ public class JumpGenerator { private int combo = 0; - private final int preview = 2; + private final int preview = 3; private static final int FULL_DIFFICULTY_AT = 1000; @@ -37,6 +37,10 @@ public class JumpGenerator { } public void generateNext() { + this.generateNext(0); + } + + private void generateNext(int animationDelayTicks) { boolean isTenth = this.blocks.size() % 10 == 0; boolean standingTenth = (this.blocks.size()-this.preview-1) % 10 == 0; boolean isHundredth = this.blocks.size() % 100 == 0; @@ -51,7 +55,7 @@ public class JumpGenerator { Pos previous = blocks.get(blocks.size()-1); Pos target = previous.add(this.nextOffset(index, r)); Block block = isHundredth ? Block.DIAMOND_BLOCK : isTenth ? Block.GOLD_BLOCK : BlockPalette.getRandom(index, r); - BlockTransition.animate(this.world, previous, target, block, () -> { + BlockTransition.animate(this.world, previous, target, block, animationDelayTicks, () -> { this.world.setBlock(target, block); this.world.setBlock(target.add(0, 1, 0), Block.LIGHT); this.player.sendPacket(new ParticlePacket(Particle.CRIT, target, target.add(0.5), 1, 10)); @@ -76,16 +80,15 @@ public class JumpGenerator { } /** - * 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. + * Abstand zum nächsten Block. Sprünge sind immer mindestens 3 weit (natürliche Sprint-Weite, + * kürzere werden überjumpt). Über die ersten {@link #FULL_DIFFICULTY_AT} Sprünge steigen der + * Anteil an 4er-Sprüngen, seitlichen Versätzen und Sprüngen nach oben bis zum bisherigen + * Niveau und bleiben 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 dz = r.nextDouble() < 0.1 + 0.4 * difficulty ? 4 : 3; int dx = r.nextDouble() < 0.1 + 0.57 * difficulty ? (r.nextBoolean() ? 1 : -1) : 0; @@ -95,7 +98,8 @@ public class JumpGenerator { 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 + // weite Sprünge nach oben erst ab mittlerer, diagonal erst ab hoher Schwierigkeit + if(dy == 1 && dz == 4 && difficulty < 0.4) dz = 3; if(dy == 1 && dz == 4 && dx != 0 && difficulty < 0.7) dx = 0; return new Pos(dx, dy, dz); @@ -111,7 +115,8 @@ public class JumpGenerator { this.checkpoint = start; this.blocks.add(this.starting); this.world.setBlock(this.starting, Block.DIAMOND_BLOCK); - for(int i = 0; i <= preview; i++) generateNext(); + // nacheinander animieren, damit jeder Block aus seinem bereits stehenden Vorgänger wächst + for(int i = 0; i <= preview; i++) generateNext(i * BlockTransition.TOTAL_TICKS); } public void reset() {