Give every stage its own time of day with smooth transitions, ambient particles and music
This commit is contained in:
1 parent
de6b987ffd
commit
137e162ea4
4 files changed
+98
-15
No files matched your search
@@ -47,8 +47,8 @@ public class EndlessJumper {
|
||||
|
||||
final InstanceContainer world = instanceManager.createInstanceContainer();
|
||||
world.setBlock(START, Block.DIAMOND_BLOCK);
|
||||
// Tageszeit steuert der jeweilige Abschnitt
|
||||
world.defaultClock().pause();
|
||||
world.setTime(18000);
|
||||
|
||||
p.setSkin(skin);
|
||||
event.setSpawningInstance(world);
|
||||
|
||||
@@ -1,58 +1,64 @@
|
||||
package eu.mhsl.minecraft.endlessjumper.generator;
|
||||
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.particle.Particle;
|
||||
import net.minestom.server.sound.SoundEvent;
|
||||
|
||||
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).
|
||||
* Abschnitte entlang des Fortschritts: einmal durch das komplette Spiel, von der Wiese über
|
||||
* Untergrund, Nether und Stronghold bis ins End (ab Sprung 1000). Jeder Abschnitt hat eigene
|
||||
* Blöcke und eine eigene Stimmung (Tageszeit, Partikel, Musik).
|
||||
*/
|
||||
public class BlockPalette {
|
||||
public record Stage(int from, String name, List<Block> blocks) {
|
||||
/** @param ambient Partikel um den Spieler, {@code null} für keine */
|
||||
public record Stage(int from, String name, long time, Particle ambient, SoundEvent music, List<Block> blocks) {
|
||||
}
|
||||
|
||||
private static final long MORNING = 1000, NOON = 6000, AFTERNOON = 11000, EVENING = 13000, NIGHT = 18000;
|
||||
|
||||
private static final List<Stage> STAGES = List.of(
|
||||
new Stage(0, "Wiese", List.of(
|
||||
new Stage(0, "Wiese", MORNING, null, SoundEvent.MUSIC_OVERWORLD_MEADOW, List.of(
|
||||
Block.GRASS_BLOCK, Block.GRASS_BLOCK, Block.MOSS_BLOCK, Block.PODZOL, Block.ROOTED_DIRT
|
||||
)),
|
||||
new Stage(75, "Wald", List.of(
|
||||
new Stage(75, "Wald", NOON, Particle.CHERRY_LEAVES, SoundEvent.MUSIC_OVERWORLD_FOREST, 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(
|
||||
new Stage(150, "Untergrund", AFTERNOON, null, SoundEvent.MUSIC_OVERWORLD_DRIPSTONE_CAVES, 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(
|
||||
new Stage(250, "Höhlen", EVENING, Particle.DRIPPING_WATER, SoundEvent.MUSIC_OVERWORLD_LUSH_CAVES, 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(
|
||||
new Stage(350, "Tiefenschiefer", NIGHT, Particle.SCULK_SOUL, SoundEvent.MUSIC_OVERWORLD_DEEP_DARK, 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(
|
||||
new Stage(450, "Netherportal", NIGHT, Particle.PORTAL, SoundEvent.MUSIC_NETHER_NETHER_WASTES, List.of(
|
||||
Block.OBSIDIAN, Block.OBSIDIAN, Block.CRYING_OBSIDIAN
|
||||
)),
|
||||
new Stage(475, "Nether", List.of(
|
||||
new Stage(475, "Nether", NIGHT, Particle.CRIMSON_SPORE, SoundEvent.MUSIC_NETHER_CRIMSON_FOREST, 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(
|
||||
new Stage(625, "Festung & Bastion", NIGHT, Particle.WHITE_ASH, SoundEvent.MUSIC_NETHER_BASALT_DELTAS, 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(
|
||||
new Stage(750, "Stronghold", NIGHT, Particle.ENCHANT, SoundEvent.MUSIC_DISC_MELLOHI, 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(
|
||||
new Stage(925, "Portalraum", NIGHT, Particle.REVERSE_PORTAL, SoundEvent.MUSIC_DRAGON, 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(
|
||||
new Stage(1000, "End", NIGHT, Particle.END_ROD, SoundEvent.MUSIC_END, List.of(
|
||||
Block.END_STONE, Block.END_STONE, Block.END_STONE_BRICKS,
|
||||
Block.PURPUR_BLOCK, Block.PURPUR_PILLAR, Block.OBSIDIAN
|
||||
))
|
||||
|
||||
@@ -60,6 +60,7 @@ public class JumpGenerator {
|
||||
private final Player player;
|
||||
private final Instance world;
|
||||
private final Random random = new Random();
|
||||
private final StageAtmosphere atmosphere;
|
||||
|
||||
private final List<Step> course = new ArrayList<>();
|
||||
private final Map<BlockKey, Integer> courseIndex = new HashMap<>();
|
||||
@@ -77,6 +78,7 @@ public class JumpGenerator {
|
||||
this.player = player;
|
||||
this.world = world;
|
||||
this.checkpoint = start;
|
||||
this.atmosphere = new StageAtmosphere(player, world, BlockPalette.stageAt(0));
|
||||
this.addStep(new Step(start, Block.DIAMOND_BLOCK));
|
||||
// nacheinander animieren, damit jeder Block aus seinem bereits stehenden Vorgänger wächst
|
||||
this.ensureGenerated(PREVIEW + 1, BlockTransition.TOTAL_TICKS);
|
||||
@@ -128,6 +130,7 @@ 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)) {
|
||||
this.atmosphere.enter(stage);
|
||||
this.player.playSound(STAGE_SOUND);
|
||||
this.player.sendActionBar(Component.text(stage.name(), NamedTextColor.GOLD));
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package eu.mhsl.minecraft.endlessjumper.generator;
|
||||
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
import net.kyori.adventure.sound.SoundStop;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.network.packet.server.play.ParticlePacket;
|
||||
import net.minestom.server.timer.Task;
|
||||
import net.minestom.server.timer.TaskSchedule;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Stimmung des aktuellen Abschnitts: Tageszeit (fließender Übergang), Musik und Partikel,
|
||||
* die dauerhaft um den Spieler schweben.
|
||||
*/
|
||||
class StageAtmosphere {
|
||||
private static final int TIME_TRANSITION_TICKS = 100;
|
||||
private static final Vec AMBIENT_SPREAD = new Vec(8, 3, 8);
|
||||
|
||||
private final Player player;
|
||||
private final Instance world;
|
||||
private BlockPalette.Stage stage;
|
||||
private Task timeTransition;
|
||||
|
||||
StageAtmosphere(Player player, Instance world, BlockPalette.Stage initial) {
|
||||
this.player = player;
|
||||
this.world = world;
|
||||
this.stage = initial;
|
||||
this.world.setTime(initial.time());
|
||||
this.world.refreshTime();
|
||||
this.playMusic();
|
||||
// an den Spieler gebunden, endet also mit ihm
|
||||
player.scheduler().buildTask(this::spawnAmbient).repeat(TaskSchedule.tick(4)).schedule();
|
||||
}
|
||||
|
||||
void enter(BlockPalette.Stage next) {
|
||||
if(next == this.stage) return;
|
||||
this.stage = next;
|
||||
this.playMusic();
|
||||
this.fadeTimeTo(next.time());
|
||||
}
|
||||
|
||||
private void playMusic() {
|
||||
this.player.stopSound(SoundStop.source(Sound.Source.MUSIC));
|
||||
this.player.playSound(Sound.sound(this.stage.music(), Sound.Source.MUSIC, 1f, 1f));
|
||||
}
|
||||
|
||||
/** Läuft immer vorwärts zur Zielzeit, Tick für Tick, damit Himmel und Licht weich wechseln. */
|
||||
private void fadeTimeTo(long target) {
|
||||
if(this.timeTransition != null) this.timeTransition.cancel();
|
||||
long start = this.world.getTime();
|
||||
long delta = Math.floorMod(target - start, 24000L);
|
||||
if(delta == 0) return;
|
||||
|
||||
this.timeTransition = this.world.scheduler().submitTask(new Supplier<>() {
|
||||
private int tick = 0;
|
||||
|
||||
@Override
|
||||
public TaskSchedule get() {
|
||||
this.tick++;
|
||||
StageAtmosphere.this.world.setTime(start + delta * this.tick / TIME_TRANSITION_TICKS);
|
||||
StageAtmosphere.this.world.refreshTime();
|
||||
return this.tick < TIME_TRANSITION_TICKS ? TaskSchedule.tick(1) : TaskSchedule.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void spawnAmbient() {
|
||||
if(this.stage.ambient() == null) return;
|
||||
this.player.sendPacket(new ParticlePacket(this.stage.ambient(), this.player.getPosition().add(0, 1.5, 0), AMBIENT_SPREAD, 0.02f, 3));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user