new blocks grow out of the previous block and glide into place

- new jump blocks spawn as a small block display inside the previous block and
  interpolate position and scale to their target over 8 ticks
- the real block (plus light and particles) is placed when the display arrives,
  the display is removed afterwards

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MineTecandClaude Opus 5.5 committed 2026-10-07 23:52:04 +02:00
1 parent 0faff2bd0c
commit 452f2f3ef7
2 files changed
+58 -5

No files matched your search

@@ -0,0 +1,49 @@
package eu.mhsl.minecraft.endlessjumper.generator;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.metadata.display.BlockDisplayMeta;
import net.minestom.server.instance.Instance;
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.
*/
public class BlockTransition {
private static final int DURATION_TICKS = 8;
private static final double START_SCALE = 0.3;
public static void animate(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
meta.setScale(new Vec(START_SCALE));
meta.setTranslation(new Vec((1 - START_SCALE) / 2));
});
display.setInstance(world, from).thenRun(() -> {
// einen Moment warten, damit der Client das Display kennt, bevor die Interpolation startet
display.scheduler().buildTask(() -> {
display.editEntityMeta(BlockDisplayMeta.class, meta -> {
meta.setTransformationInterpolationStartDelta(0);
meta.setScale(Vec.ONE);
meta.setTranslation(Vec.ZERO);
});
display.teleport(to);
}).delay(TaskSchedule.tick(2)).schedule();
display.scheduler().buildTask(() -> {
onArrival.run();
display.remove();
}).delay(TaskSchedule.tick(DURATION_TICKS + 3)).schedule();
});
}
}
@@ -48,8 +48,15 @@ public class JumpGenerator {
Random r = new Random();
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));
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, () -> {
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));
this.player.sendPacket(new ParticlePacket(Particle.FALLING_NECTAR, target, target.add(0.5), 0.3f, 10));
});
if(standingTenth) {
Pos current = this.blocks.get(this.blocks.size()-this.preview-1);
@@ -64,10 +71,7 @@ public class JumpGenerator {
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));
this.player.sendPacket(new ParticlePacket(Particle.FALLING_NECTAR, target, target.add(0.5),0.3f, 10));
this.combo++;
}