feat: add Duels mode (MultiMode, generator-per-player)

- duels/: DuelsMode (MultiMode), DuelsGenerator (owner: per-player
  SingleDuelsGenerator map, allowJoining, goal), SingleDuelsGenerator
  (getPlayers()=List.of(own player) -> independent course/score; score
  reaches goal -> win), DuelsCountdown (10s countdown + race start)
- win(): sorted scoreboard, victory/loss titles, return to default after 10s;
  leave() only removePlayer (no Coop recursion)
- config modes.duels.{enabled,max-players,goal,island-distance}, locales
  play.multi.duels.* (ru/en), registered in Modes/PluginBootstrap
- checkstyle 0/0, PMD 0, build green
This commit is contained in:
loki5512344 2026-08-05 21:16:00 +02:00
parent 8fde1b579b
commit acb884b70b
Signed by: boba
GPG key ID: 253067914055423B
11 changed files with 494 additions and 3 deletions

View file

@ -0,0 +1,86 @@
package dev.loki.loparkour.duels;
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
import dev.loki.loparkour.mode.base.Mode;
import dev.loki.loparkour.mode.base.Modes;
import dev.loki.loparkour.player.core.ParkourPlayer;
import dev.loki.loparkour.session.core.Session;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* One parkour course per duels player, isolated from everyone else.
*
* <p>Scoring only looks at the owned player: {@link #getPlayers()} returns just
* the owned player, so {@code state.history}/{@code state.score} belong to that
* single course. The owning {@link DuelsGenerator} ticks every instance.</p>
*/
public class SingleDuelsGenerator extends ParkourGenerator {
private static final List<Material> COLORS = List.of(
Material.LIGHT_BLUE_CONCRETE, Material.RED_CONCRETE, Material.LIME_CONCRETE, Material.YELLOW_CONCRETE,
Material.ORANGE_CONCRETE, Material.BLUE_CONCRETE, Material.MAGENTA_CONCRETE, Material.WHITE_CONCRETE,
Material.LIGHT_GRAY_CONCRETE, Material.BLACK_CONCRETE, Material.GREEN_CONCRETE, Material.BROWN_CONCRETE,
Material.CYAN_CONCRETE, Material.PURPLE_CONCRETE, Material.GRAY_CONCRETE, Material.PINK_CONCRETE);
public DuelsGenerator owningGenerator;
private ParkourPlayer ownedPlayer;
private BlockData blockData;
private boolean won;
public SingleDuelsGenerator(@NotNull Session session) {
super(session);
}
public void setOwnedPlayer(@NotNull ParkourPlayer pp) {
this.ownedPlayer = pp;
}
public void setPlayerIndex(int index) {
this.blockData = COLORS.get(index % COLORS.size()).createBlockData();
}
@Override
@NotNull
public List<ParkourPlayer> getPlayers() {
return ownedPlayer == null ? List.of() : List.of(ownedPlayer);
}
@Override
public void startTick() {
}
@Override
public void tick() {
super.tick();
checkWin();
}
@Override
protected void score() {
super.score();
checkWin();
}
/**
* Duels has no persistent leaderboard; falling must not register anything.
*/
@Override
public void registerScore(String time, String difficulty, int score) {
}
private void checkWin() {
if (!won && owningGenerator != null && state.score >= owningGenerator.goal) {
won = true;
owningGenerator.win(ownedPlayer);
}
}
@Override
public Mode getMode() {
return Modes.DUELS;
}
}