fix jump slab
This commit is contained in:
parent
d9eb747c1b
commit
978f739045
43 changed files with 5685 additions and 832 deletions
|
|
@ -1,10 +1,8 @@
|
|||
package dev.loki.loparkour.mode;
|
||||
|
||||
import dev.loki.loparkour.util.Item;
|
||||
|
||||
import dev.lolib.scheduler.Scheduler;
|
||||
import dev.lolib.scheduler.ScheduledTask;
|
||||
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.config.Config;
|
||||
import dev.loki.loparkour.config.Locales;
|
||||
|
|
@ -12,7 +10,6 @@ import dev.loki.loparkour.generator.ParkourGenerator;
|
|||
import dev.loki.loparkour.leaderboard.Leaderboard;
|
||||
import dev.loki.loparkour.player.ParkourPlayer;
|
||||
import dev.loki.loparkour.session.Session;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.Sound;
|
||||
|
|
@ -24,6 +21,20 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Speedrun Mode — blocks disappear a short time after the player lands on them.
|
||||
*
|
||||
* <p>Bugs fixed vs previous version:
|
||||
* <ul>
|
||||
* <li>{@code onBlockTouch()} was never called from anywhere — blocks never disappeared.
|
||||
* Fixed by hooking into {@link #score()} which fires exactly when the player
|
||||
* steps onto a new block for the first time.</li>
|
||||
* <li>Warning task reference was stored in a throwaway local, not in scheduledTasks,
|
||||
* so it could never be cancelled on reset.</li>
|
||||
* <li>{@code removalTime / 50} used integer division that truncated sub-second values
|
||||
* to 0 ticks when blockLifetime < 0.05s. Now uses Math.max(1, ...).</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class SpeedrunMode implements Mode {
|
||||
|
||||
private final Leaderboard leaderboard = new Leaderboard(getName(), Leaderboard.Sort.SCORE);
|
||||
|
|
@ -36,7 +47,7 @@ public class SpeedrunMode implements Mode {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
public dev.loki.loparkour.util.Item getItem(String locale) {
|
||||
public Item getItem(String locale) {
|
||||
return Locales.getItem(locale, "play.single.speedrun");
|
||||
}
|
||||
|
||||
|
|
@ -54,72 +65,84 @@ public class SpeedrunMode implements Mode {
|
|||
}
|
||||
|
||||
ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof SpeedrunMode) {
|
||||
if (pp != null && pp.session.generator instanceof SpeedrunGenerator) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.closeInventory();
|
||||
|
||||
Session.create(session -> new SpeedrunGenerator(session), null, null, player);
|
||||
}
|
||||
|
||||
public static class SpeedrunGenerator extends ParkourGenerator {
|
||||
private final Map<Block, Long> blockTimestamps = new HashMap<>();
|
||||
private final Map<Block, ScheduledTask> scheduledTasks = new HashMap<>();
|
||||
// ── SpeedrunGenerator ─────────────────────────────────────────────────────
|
||||
|
||||
private static class SpeedrunGenerator extends ParkourGenerator {
|
||||
|
||||
/** Tracks scheduled tasks per block. Key = block, value = [warningTask, removalTask]. */
|
||||
private final Map<Block, ScheduledTask[]> scheduledTasks = new HashMap<>();
|
||||
|
||||
public SpeedrunGenerator(@NotNull Session session) {
|
||||
super(session);
|
||||
|
||||
// Notify player
|
||||
for (ParkourPlayer pp : session.getPlayers()) {
|
||||
pp.player.sendTitle("§c§lSPEEDRUN MODE", "§7Blocks disappear after you step on them!", 10, 70, 20);
|
||||
pp.player.sendMessage("§c§lSpeedrun Mode §7activated! Don't look back!");
|
||||
}
|
||||
}
|
||||
|
||||
public void onBlockTouch(@NotNull Block block) {
|
||||
if (!blockTimestamps.containsKey(block)) {
|
||||
blockTimestamps.put(block, System.currentTimeMillis());
|
||||
scheduleBlockRemoval(block);
|
||||
}
|
||||
/**
|
||||
* Called every time the player scores a point (= lands on a new block).
|
||||
* We hook here instead of a separate onBlockTouch() because score() is the
|
||||
* canonical place where "player stepped on block N" is detected.
|
||||
*/
|
||||
@Override
|
||||
protected void score() {
|
||||
super.score();
|
||||
|
||||
// The block the player just landed on is the previous-to-last in history
|
||||
// (history.size()-1 is the leading block, size()-2 is where player stands)
|
||||
int histSize = state.history.size();
|
||||
if (histSize < 2) return;
|
||||
|
||||
Block stepped = state.history.get(histSize - 2);
|
||||
if (stepped.getType() == Material.AIR) return;
|
||||
if (scheduledTasks.containsKey(stepped)) return; // already scheduled
|
||||
|
||||
scheduleBlockRemoval(stepped);
|
||||
}
|
||||
|
||||
private void scheduleBlockRemoval(@NotNull Block block) {
|
||||
double blockLifetime = Config.CONFIG.getDouble("modes.speedrun.block-lifetime");
|
||||
double warningTime = Config.CONFIG.getDouble("modes.speedrun.warning-time");
|
||||
double warningTime = Config.CONFIG.getDouble("modes.speedrun.warning-time");
|
||||
|
||||
long removalTime = (long) (blockLifetime * 1000);
|
||||
long warningTimeMs = (long) (warningTime * 1000);
|
||||
// Convert seconds → ticks (20 ticks/s), minimum 1 tick
|
||||
long removalTicks = Math.max(1, Math.round(blockLifetime * 20));
|
||||
long warningTicks = Math.max(1, Math.round(warningTime * 20));
|
||||
|
||||
ScheduledTask warningTask = Scheduler.get(LoParkour.getPlugin()).runLater(() -> {
|
||||
if (block.getType() != Material.AIR) {
|
||||
player.player.playSound(block.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f);
|
||||
player.player.spawnParticle(Particle.FLAME, block.getLocation().add(0.5, 1, 0.5), 10, 0.3, 0.3, 0.3, 0.01);
|
||||
}
|
||||
}, warningTimeMs / 50);
|
||||
if (block.getType() != Material.AIR) {
|
||||
player.player.playSound(block.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f);
|
||||
player.player.spawnParticle(Particle.FLAME,
|
||||
block.getLocation().add(0.5, 1, 0.5), 8, 0.2, 0.2, 0.2, 0.01);
|
||||
}
|
||||
}, warningTicks);
|
||||
|
||||
ScheduledTask removalTask = Scheduler.get(LoParkour.getPlugin()).runLater(() -> {
|
||||
if (block.getType() != Material.AIR) {
|
||||
block.setType(Material.AIR);
|
||||
blockTimestamps.remove(block);
|
||||
scheduledTasks.remove(block);
|
||||
}
|
||||
}, removalTime / 50);
|
||||
if (block.getType() != Material.AIR) {
|
||||
block.setType(Material.AIR, false);
|
||||
}
|
||||
scheduledTasks.remove(block);
|
||||
}, removalTicks);
|
||||
|
||||
scheduledTasks.put(block, removalTask);
|
||||
// Store both so we can cancel them on reset
|
||||
scheduledTasks.put(block, new ScheduledTask[]{warningTask, removalTask});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(boolean regenerate) {
|
||||
// Cancel all scheduled tasks
|
||||
for (ScheduledTask task : scheduledTasks.values()) {
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
// Cancel every pending warning and removal task
|
||||
scheduledTasks.values().forEach(tasks -> {
|
||||
for (ScheduledTask t : tasks) {
|
||||
if (t != null) t.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
scheduledTasks.clear();
|
||||
blockTimestamps.clear();
|
||||
|
||||
|
||||
super.reset(regenerate);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue