Add feather fall and spring jokers, drop jokers on checkpoints with 10% chance and only give a totem when starting from zero
This commit is contained in:
1 parent
555f4024e4
commit
38bb9c6dc8
3 files changed
+54
-21
No files matched your search
@@ -19,11 +19,9 @@ import net.minestom.server.event.player.PlayerMoveEvent;
|
|||||||
import net.minestom.server.event.player.PlayerSpawnEvent;
|
import net.minestom.server.event.player.PlayerSpawnEvent;
|
||||||
import net.minestom.server.event.player.PlayerSwapItemEvent;
|
import net.minestom.server.event.player.PlayerSwapItemEvent;
|
||||||
import net.minestom.server.event.player.PlayerUseItemEvent;
|
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.Instance;
|
||||||
import net.minestom.server.instance.InstanceContainer;
|
import net.minestom.server.instance.InstanceContainer;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.item.ItemStack;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -54,7 +52,7 @@ public class EndlessJumper {
|
|||||||
Player p = event.getPlayer();
|
Player p = event.getPlayer();
|
||||||
p.setCanPickupItem(true);
|
p.setCanPickupItem(true);
|
||||||
ScoreTab.join(p);
|
ScoreTab.join(p);
|
||||||
Jokers.giveStart(p);
|
Jokers.clear(p);
|
||||||
// sobald der Spieler in der Welt steht, geht es los
|
// sobald der Spieler in der Welt steht, geht es los
|
||||||
games.put(p, new JumpGenerator(START, Progress.checkpoint(p.getUuid()), p, p.getInstance()));
|
games.put(p, new JumpGenerator(START, Progress.checkpoint(p.getUuid()), p, p.getInstance()));
|
||||||
});
|
});
|
||||||
@@ -70,9 +68,13 @@ public class EndlessJumper {
|
|||||||
eventHandler.addListener(ItemDropEvent.class, event -> event.setCancelled(true));
|
eventHandler.addListener(ItemDropEvent.class, event -> event.setCancelled(true));
|
||||||
eventHandler.addListener(PlayerSwapItemEvent.class, event -> event.setCancelled(true));
|
eventHandler.addListener(PlayerSwapItemEvent.class, event -> event.setCancelled(true));
|
||||||
|
|
||||||
// Rechtsklick auf Luft und auf Blöcke: useBridge() baut beim zweiten Event nichts doppelt
|
// der Client meldet Rechtsklicks auch auf Blöcke als Item-Nutzung, solange der Block sie nicht
|
||||||
eventHandler.addListener(PlayerUseItemEvent.class, event -> useJoker(event.getPlayer(), event.getItemStack()));
|
// selbst verarbeitet (auf der Strecke nie) – ein Event genügt, Joker werden nicht doppelt verbraucht
|
||||||
eventHandler.addListener(PlayerUseItemOnBlockEvent.class, event -> useJoker(event.getPlayer(), event.getItemStack()));
|
eventHandler.addListener(PlayerUseItemEvent.class, event -> {
|
||||||
|
JumpGenerator generator = games.get(event.getPlayer());
|
||||||
|
Jokers joker = Jokers.of(event.getItemStack());
|
||||||
|
if(generator != null && joker != null && generator.useJoker(joker)) joker.take(event.getPlayer());
|
||||||
|
});
|
||||||
|
|
||||||
eventHandler.addListener(PickupItemEvent.class, event -> {
|
eventHandler.addListener(PickupItemEvent.class, event -> {
|
||||||
Jokers joker = Jokers.of(event.getItemStack());
|
Jokers joker = Jokers.of(event.getItemStack());
|
||||||
@@ -119,17 +121,12 @@ public class EndlessJumper {
|
|||||||
InstanceContainer world = createWorld();
|
InstanceContainer world = createWorld();
|
||||||
player.setVelocity(Vec.ZERO);
|
player.setVelocity(Vec.ZERO);
|
||||||
player.setInstance(world, SPAWN).thenRun(() -> {
|
player.setInstance(world, SPAWN).thenRun(() -> {
|
||||||
Jokers.giveStart(player);
|
Jokers.clear(player);
|
||||||
games.put(player, new JumpGenerator(START, 0, player, world));
|
games.put(player, new JumpGenerator(START, 0, player, world));
|
||||||
releaseLater(oldWorld);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wie bei Velocity: Secret aus forwarding.secret im Arbeitsverzeichnis, alternativ per Umgebungsvariable
|
// Wie bei Velocity: Secret aus forwarding.secret im Arbeitsverzeichnis, alternativ per Umgebungsvariable
|
||||||
private static String readForwardingSecret() throws IOException {
|
private static String readForwardingSecret() throws IOException {
|
||||||
String fromEnv = System.getenv("VELOCITY_FORWARDING_SECRET");
|
String fromEnv = System.getenv("VELOCITY_FORWARDING_SECRET");
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ import net.minestom.server.item.Material;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Joker-Items in der Hotbar, die Anzahl ist die Stapelgröße.
|
* Joker-Items in der Hotbar, die Anzahl ist die Stapelgröße. Es gibt sie zum Einsammeln auf der
|
||||||
* Brücke: Rechtsklick baut einen Weg über die nächsten 10 Blöcke.
|
* Strecke; wer bei 0 beginnt, bekommt ein Totem. Das Totem wirkt automatisch, die anderen per Rechtsklick (siehe JumpGenerator#useJoker).
|
||||||
* Totem: rettet automatisch beim Runterfallen (Plattform mit Leiter).
|
|
||||||
*/
|
*/
|
||||||
public enum Jokers {
|
public enum Jokers {
|
||||||
BRIDGE(0, Material.BRICK, "Brücke", NamedTextColor.AQUA, "Rechtsklick: Weg über die nächsten 10 Blöcke"),
|
BRIDGE(0, Material.BRICK, "Brücke", NamedTextColor.AQUA, "Rechtsklick: Weg über die nächsten 10 Blöcke"),
|
||||||
|
FEATHER_FALL(1, Material.FEATHER, "Federfall", NamedTextColor.WHITE, "Rechtsklick: 15 Sekunden langsames Fallen"),
|
||||||
|
SPRING(2, Material.RABBIT_FOOT, "Sprungfeder", NamedTextColor.GREEN, "Rechtsklick: die nächsten 10 Sprünge mit Sprungkraft"),
|
||||||
TOTEM(8, Material.TOTEM_OF_UNDYING, "Rettungstotem", NamedTextColor.GOLD, "Rettet dich automatisch beim Runterfallen");
|
TOTEM(8, Material.TOTEM_OF_UNDYING, "Rettungstotem", NamedTextColor.GOLD, "Rettet dich automatisch beim Runterfallen");
|
||||||
|
|
||||||
private final int slot;
|
private final int slot;
|
||||||
@@ -52,8 +53,9 @@ public enum Jokers {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void giveStart(Player player) {
|
/** Zum Start (und nach /reset) gibt es keine Joker. */
|
||||||
for(Jokers joker : values()) player.getInventory().setItemStack(joker.slot, joker.item);
|
public static void clear(Player player) {
|
||||||
|
for(Jokers joker : values()) player.getInventory().setItemStack(joker.slot, ItemStack.AIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Jokers random(Random random) {
|
public static Jokers random(Random random) {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import net.minestom.server.instance.Instance;
|
|||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.network.packet.server.play.ParticlePacket;
|
import net.minestom.server.network.packet.server.play.ParticlePacket;
|
||||||
import net.minestom.server.particle.Particle;
|
import net.minestom.server.particle.Particle;
|
||||||
|
import net.minestom.server.potion.Potion;
|
||||||
|
import net.minestom.server.potion.PotionEffect;
|
||||||
import net.minestom.server.sound.SoundEvent;
|
import net.minestom.server.sound.SoundEvent;
|
||||||
import net.minestom.server.timer.TaskSchedule;
|
import net.minestom.server.timer.TaskSchedule;
|
||||||
|
|
||||||
@@ -51,6 +53,9 @@ public class JumpGenerator {
|
|||||||
private static final int BRIDGE_LENGTH = 10;
|
private static final int BRIDGE_LENGTH = 10;
|
||||||
private static final Block RESCUE_BLOCK = Block.OAK_PLANKS;
|
private static final Block RESCUE_BLOCK = Block.OAK_PLANKS;
|
||||||
private static final int RESCUE_DEPTH = 6;
|
private static final int RESCUE_DEPTH = 6;
|
||||||
|
private static final int FEATHER_FALL_TICKS = 15 * 20;
|
||||||
|
private static final int SPRING_JUMPS = 10;
|
||||||
|
private static final double CHECKPOINT_JOKER_CHANCE = 0.1;
|
||||||
|
|
||||||
private static final TextColor COMBO_COLOR = TextColor.color(255, 69, 0);
|
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 Title CHECKPOINT_TITLE = Title.title(Component.empty(), Component.text("Checkpoint", NamedTextColor.GREEN));
|
||||||
@@ -59,6 +64,7 @@ public class JumpGenerator {
|
|||||||
private static final Sound STAGE_SOUND = Sound.sound(SoundEvent.UI_TOAST_CHALLENGE_COMPLETE, Sound.Source.MASTER, 0.4f, 1f);
|
private static final Sound STAGE_SOUND = Sound.sound(SoundEvent.UI_TOAST_CHALLENGE_COMPLETE, Sound.Source.MASTER, 0.4f, 1f);
|
||||||
private static final Sound BRIDGE_SOUND = Sound.sound(SoundEvent.BLOCK_AMETHYST_BLOCK_CHIME, Sound.Source.MASTER, 1f, 1f);
|
private static final Sound BRIDGE_SOUND = Sound.sound(SoundEvent.BLOCK_AMETHYST_BLOCK_CHIME, Sound.Source.MASTER, 1f, 1f);
|
||||||
private static final Sound RESCUE_SOUND = Sound.sound(SoundEvent.ITEM_TOTEM_USE, Sound.Source.MASTER, 0.6f, 1f);
|
private static final Sound RESCUE_SOUND = Sound.sound(SoundEvent.ITEM_TOTEM_USE, Sound.Source.MASTER, 0.6f, 1f);
|
||||||
|
private static final Sound JOKER_SOUND = Sound.sound(SoundEvent.ENTITY_EXPERIENCE_ORB_PICKUP, Sound.Source.MASTER, 0.8f, 1.2f);
|
||||||
private static final Sound RESET_SOUND = Sound.sound(SoundEvent.BLOCK_GLASS_BREAK, Sound.Source.BLOCK, 0.3f, 1f);
|
private static final Sound RESET_SOUND = Sound.sound(SoundEvent.BLOCK_GLASS_BREAK, Sound.Source.BLOCK, 0.3f, 1f);
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
@@ -77,6 +83,9 @@ public class JumpGenerator {
|
|||||||
private int bridgeStart = -1;
|
private int bridgeStart = -1;
|
||||||
private int bridgeEnd = -1;
|
private int bridgeEnd = -1;
|
||||||
|
|
||||||
|
// Sprungfeder: so viele Sprünge noch mit Sprungkraft
|
||||||
|
private int springJumpsLeft = 0;
|
||||||
|
|
||||||
// laufende Rettung durch das Totem: der Block, auf den man über die Leiter zurückklettert
|
// laufende Rettung durch das Totem: der Block, auf den man über die Leiter zurückklettert
|
||||||
private Pos rescueSafe;
|
private Pos rescueSafe;
|
||||||
|
|
||||||
@@ -94,7 +103,9 @@ public class JumpGenerator {
|
|||||||
this.addStep(new Step(start, startBlock));
|
this.addStep(new Step(start, startBlock));
|
||||||
this.player.setLevel(startIndex);
|
this.player.setLevel(startIndex);
|
||||||
ScoreTab.update(this.player, startIndex);
|
ScoreTab.update(this.player, startIndex);
|
||||||
|
// wer ganz von vorn beginnt, bekommt ein Totem; beim Weiterspielen gibt es keine Joker
|
||||||
if(startIndex > 0) this.player.sendMessage(Component.text("Weiter ab Sprung " + startIndex, NamedTextColor.GOLD));
|
if(startIndex > 0) this.player.sendMessage(Component.text("Weiter ab Sprung " + startIndex, NamedTextColor.GOLD));
|
||||||
|
else Jokers.TOTEM.add(this.player);
|
||||||
|
|
||||||
// nacheinander animieren, damit jeder Block aus seinem bereits stehenden Vorgänger wächst
|
// nacheinander animieren, damit jeder Block aus seinem bereits stehenden Vorgänger wächst
|
||||||
this.ensureGenerated(startIndex + PREVIEW + 1, BlockTransition.TOTAL_TICKS);
|
this.ensureGenerated(startIndex + PREVIEW + 1, BlockTransition.TOTAL_TICKS);
|
||||||
@@ -139,6 +150,8 @@ public class JumpGenerator {
|
|||||||
this.player.setLevel(index);
|
this.player.setLevel(index);
|
||||||
ScoreTab.update(this.player, index);
|
ScoreTab.update(this.player, index);
|
||||||
|
|
||||||
|
if(this.springJumpsLeft > 0 && --this.springJumpsLeft == 0) this.player.removeEffect(PotionEffect.JUMP_BOOST);
|
||||||
|
|
||||||
if(index < this.bridgeStart || index > this.bridgeEnd) {
|
if(index < this.bridgeStart || index > this.bridgeEnd) {
|
||||||
this.combo++;
|
this.combo++;
|
||||||
this.player.playSound(Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_PLING, Sound.Source.MASTER, 0.1f, 1 + this.combo * 0.025f));
|
this.player.playSound(Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_PLING, Sound.Source.MASTER, 0.1f, 1 + this.combo * 0.025f));
|
||||||
@@ -204,12 +217,12 @@ public class JumpGenerator {
|
|||||||
this.world.setBlock(target.add(0, 1, 0), Block.LIGHT);
|
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.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.player.sendPacket(new ParticlePacket(Particle.FALLING_NECTAR, target, target.add(0.5), 0.3f, 10));
|
||||||
if(isMilestone(index)) this.dropJoker(target);
|
if(isMilestone(index) || (isCheckpoint(index) && this.random.nextDouble() < CHECKPOINT_JOKER_CHANCE)) this.dropJoker(target);
|
||||||
if(afterArrival != null) afterArrival.run();
|
if(afterArrival != null) afterArrival.run();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Auf jedem Diamantblock liegt ein zufälliger Joker zum Einsammeln. */
|
/** Auf jedem Diamantblock (und manchem Checkpoint) liegt ein zufälliger Joker zum Einsammeln. */
|
||||||
private void dropJoker(Pos target) {
|
private void dropJoker(Pos target) {
|
||||||
ItemEntity item = new ItemEntity(Jokers.random(this.random).item());
|
ItemEntity item = new ItemEntity(Jokers.random(this.random).item());
|
||||||
item.setPickupDelay(Duration.ZERO);
|
item.setPickupDelay(Duration.ZERO);
|
||||||
@@ -244,12 +257,31 @@ public class JumpGenerator {
|
|||||||
return new Vec(dx, dy, dz);
|
return new Vec(dx, dy, dz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Rechtsklick mit einem Joker; true, wenn er verbraucht wurde. Das Totem wirkt nur automatisch. */
|
||||||
|
public boolean useJoker(Jokers joker) {
|
||||||
|
boolean used = switch(joker) {
|
||||||
|
case BRIDGE -> this.useBridge();
|
||||||
|
case FEATHER_FALL -> {
|
||||||
|
this.player.addEffect(new Potion(PotionEffect.SLOW_FALLING, 0, FEATHER_FALL_TICKS));
|
||||||
|
yield true;
|
||||||
|
}
|
||||||
|
case SPRING -> {
|
||||||
|
this.springJumpsLeft = SPRING_JUMPS;
|
||||||
|
this.player.addEffect(new Potion(PotionEffect.JUMP_BOOST, 0, Potion.INFINITE_DURATION));
|
||||||
|
yield true;
|
||||||
|
}
|
||||||
|
case TOTEM -> false;
|
||||||
|
};
|
||||||
|
if(used) this.player.playSound(JOKER_SOUND);
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Joker „Brücke“: verbindet die nächsten {@link #BRIDGE_LENGTH} Blöcke ab der aktuellen Position
|
* Joker „Brücke“: verbindet die nächsten {@link #BRIDGE_LENGTH} Blöcke ab der aktuellen Position
|
||||||
* mit einem begehbaren Weg, auch über früher schon geschaffte Stücke (z. B. nach einem Reset).
|
* mit einem begehbaren Weg, auch über früher schon geschaffte Stücke (z. B. nach einem Reset).
|
||||||
* Diese Blöcke zählen nicht zur Combo. Ist der Bereich schon überbrückt, passiert nichts.
|
* Diese Blöcke zählen nicht zur Combo. Ist der Bereich schon überbrückt, passiert nichts.
|
||||||
*/
|
*/
|
||||||
public boolean useBridge() {
|
private boolean useBridge() {
|
||||||
int target = this.standing + BRIDGE_LENGTH;
|
int target = this.standing + BRIDGE_LENGTH;
|
||||||
if(this.rescueSafe != null || (this.standing >= this.bridgeStart && target <= this.bridgeEnd)) return false;
|
if(this.rescueSafe != null || (this.standing >= this.bridgeStart && target <= this.bridgeEnd)) return false;
|
||||||
this.bridgeStart = this.standing;
|
this.bridgeStart = this.standing;
|
||||||
@@ -334,9 +366,11 @@ public class JumpGenerator {
|
|||||||
if(existing.compare(Block.AIR) || existing.compare(BRIDGE_BLOCK)) this.world.setBlock(pos, block);
|
if(existing.compare(Block.AIR) || existing.compare(BRIDGE_BLOCK)) this.world.setBlock(pos, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Beendet alles, was an den Spieler gebunden weiterläuft (Partikel, Musik). */
|
/** Beendet alles, was an den Spieler gebunden weiterläuft (Partikel, Musik, Joker-Effekte). */
|
||||||
public void close() {
|
public void close() {
|
||||||
this.atmosphere.stop();
|
this.atmosphere.stop();
|
||||||
|
this.player.removeEffect(PotionEffect.SLOW_FALLING);
|
||||||
|
this.player.removeEffect(PotionEffect.JUMP_BOOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset() {
|
private void reset() {
|
||||||
|
|||||||
Reference in new issue
Block a user