From 4c255111f0d04b00c5e6dbf4d22641cebb6d0b1f Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Thu, 6 Aug 2026 10:59:30 +0200 Subject: [PATCH] fix: coop session creation, schematic cleanup, wire dead generator config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - coop: create() builds a real CoopGenerator session (host + lobby), join() tracks contributions (п.23) - schematics: removeSchematicBlocks() airs and prunes schematic blocks instead of removeTrailBlocks(0) (п.34) - config: wire jump-validation into JumpValidator, useSpecialBlocks gates special chances, blockLead drives generation lead; remove dead jump-types keys and EffectManager (п.38) --- .../loki/loparkour/config/options/Option.java | 6 - .../options/section/OptionGeneration.java | 28 +--- .../coordinator/GeneratorProfileManager.java | 6 + .../generator/effect/EffectManager.java | 95 ------------- .../jump/calculation/JumpValidator.java | 8 +- .../lifecycle/loop/LifecycleTickManager.java | 9 +- .../lifecycle/player/GeneratorCleanup.java | 34 +++-- .../player/PlayerInteractionHandler.java | 12 +- .../mode/impl/multi/CoopGenerator.java | 89 ++++++++++++ .../loparkour/mode/impl/multi/CoopMode.java | 134 +++--------------- src/main/resources/config.yml | 20 --- src/main/resources/generation.yml | 11 -- 12 files changed, 151 insertions(+), 301 deletions(-) delete mode 100644 src/main/java/dev/loki/loparkour/generator/effect/EffectManager.java create mode 100644 src/main/java/dev/loki/loparkour/mode/impl/multi/CoopGenerator.java diff --git a/src/main/java/dev/loki/loparkour/config/options/Option.java b/src/main/java/dev/loki/loparkour/config/options/Option.java index ce94ad0..8032fa9 100644 --- a/src/main/java/dev/loki/loparkour/config/options/Option.java +++ b/src/main/java/dev/loki/loparkour/config/options/Option.java @@ -95,9 +95,6 @@ public class Option { public static double MAX_HORIZONTAL_DISTANCE; public static double MAX_VERTICAL_UP; public static double MAX_VERTICAL_DOWN; - public static boolean JUMP_TYPES_ENABLED; - public static Map JUMP_TYPE_ENABLED; - public static Map JUMP_TYPE_CHANCE; // General public static double BORDER_SIZE; @@ -154,9 +151,6 @@ public class Option { MAX_HORIZONTAL_DISTANCE = OptionGeneration.MAX_HORIZONTAL_DISTANCE; MAX_VERTICAL_UP = OptionGeneration.MAX_VERTICAL_UP; MAX_VERTICAL_DOWN = OptionGeneration.MAX_VERTICAL_DOWN; - JUMP_TYPES_ENABLED = OptionGeneration.JUMP_TYPES_ENABLED; - JUMP_TYPE_ENABLED = OptionGeneration.JUMP_TYPE_ENABLED; - JUMP_TYPE_CHANCE = OptionGeneration.JUMP_TYPE_CHANCE; // General BORDER_SIZE = OptionGeneral.BORDER_SIZE; diff --git a/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java b/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java index f18fb09..7df449e 100644 --- a/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java +++ b/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java @@ -3,11 +3,8 @@ package dev.loki.loparkour.config.options.section; import dev.loki.loparkour.LoParkour; import dev.loki.loparkour.config.core.Config; -import java.util.HashMap; -import java.util.Map; - /** - * Generation-related configuration options (jump types, validation, memory, ghost mode). + * Generation-related configuration options (validation, memory, ghost mode). */ public class OptionGeneration { @@ -62,14 +59,8 @@ public class OptionGeneration { public static double MAX_VERTICAL_UP; public static double MAX_VERTICAL_DOWN; - // ── Jump types ──────────────────────────────────────────────────────────── - public static boolean JUMP_TYPES_ENABLED; - public static Map JUMP_TYPE_ENABLED; - public static Map JUMP_TYPE_CHANCE; - public static void init() { initJumpValidation(); - initJumpTypes(); initMemory(); initGhostMode(); initGenerationProbabilities(); @@ -88,23 +79,6 @@ public class OptionGeneration { ? Config.CONFIG.getDouble("jump-validation.max-vertical-down") : DEFAULT_MAX_VERTICAL_DOWN; } - private static void initJumpTypes() { - JUMP_TYPES_ENABLED = Config.CONFIG.isPath("jump-types.enabled") - && Config.CONFIG.getBoolean("jump-types.enabled"); - JUMP_TYPE_ENABLED = new HashMap<>(); - JUMP_TYPE_CHANCE = new HashMap<>(); - - if (Config.CONFIG.isPath("jump-types.types")) { - for (final String type : Config.CONFIG.getChildren("jump-types.types")) { - final String p = "jump-types.types." + type; - JUMP_TYPE_ENABLED.put(type, Config.CONFIG.isPath(p + ".enabled") - && Config.CONFIG.getBoolean(p + ".enabled")); - JUMP_TYPE_CHANCE.put(type, Config.CONFIG.isPath(p + ".chance") - ? Config.CONFIG.getDouble(p + ".chance") : 0.0); - } - } - } - private static void initMemory() { BLOCK_CLEANUP_DISTANCE = Config.CONFIG.isPath("memory.block-cleanup-distance") ? Config.CONFIG.getInt("memory.block-cleanup-distance") : DEFAULT_BLOCK_CLEANUP_DISTANCE; diff --git a/src/main/java/dev/loki/loparkour/generator/core/coordinator/GeneratorProfileManager.java b/src/main/java/dev/loki/loparkour/generator/core/coordinator/GeneratorProfileManager.java index ccc2c17..ad63d5c 100644 --- a/src/main/java/dev/loki/loparkour/generator/core/coordinator/GeneratorProfileManager.java +++ b/src/main/java/dev/loki/loparkour/generator/core/coordinator/GeneratorProfileManager.java @@ -127,6 +127,12 @@ public class GeneratorProfileManager { generator.state.defaultChances.put(ParkourGenerator.BlockGenerationType.SPECIAL, (double) specialChance); generator.state.defaultChances.put(ParkourGenerator.BlockGenerationType.SCHEMATIC, (double) schematicChance); + final boolean specialsEnabled = !generator.profile.settings.containsKey("useSpecialBlocks") + || generator.profile.get("useSpecialBlocks").asBoolean(); + if (!specialsEnabled) { + generator.state.defaultChances.put(ParkourGenerator.BlockGenerationType.SPECIAL, 0.0); + } + normalizeMap(generator.state.defaultChances); } diff --git a/src/main/java/dev/loki/loparkour/generator/effect/EffectManager.java b/src/main/java/dev/loki/loparkour/generator/effect/EffectManager.java deleted file mode 100644 index 6b74207..0000000 --- a/src/main/java/dev/loki/loparkour/generator/effect/EffectManager.java +++ /dev/null @@ -1,95 +0,0 @@ -package dev.loki.loparkour.generator.effect; - -import dev.loki.loparkour.config.options.Option; -import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator; -import dev.loki.loparkour.player.core.ParkourPlayer; -import dev.loki.loparkour.util.particle.ParticleUtil; -import org.bukkit.Location; -import org.bukkit.block.Block; - -import java.util.List; - -/** - * Handles particle and sound effects for the parkour generator. - * Extracted from ParkourGenerator to keep it focused on generation logic. - */ -public class EffectManager { - - private static final int DOT_PARTICLE_COUNT = 5; - private static final double DOT_PARTICLE_OFFSET = 0.1; - private static final double CIRCLE_CENTER_OFFSET = 0.5; - private static final int CIRCLE_RADIUS = 8; - private static final double BOX_PARTICLE_SPACING = 0.3; - - private final ParkourGenerator generator; - - public EffectManager(ParkourGenerator generator) { - this.generator = generator; - } - - /** - * Spawns particles around the given blocks based on the configured shape. - */ - public void particles(List blocks) { - if (!generator.profile.get("particles").asBoolean()) { - return; - } - - final List locations = blocks.stream().map(Block::getLocation).toList(); - final Location max = locations.stream().reduce((a, b) -> { - final double x = Math.max(a.getX(), b.getX()); - final double y = Math.max(a.getY(), b.getY()); - final double z = Math.max(a.getZ(), b.getZ()); - return new Location(a.getWorld(), x, y, z); - }).orElse(null); - final Location min = locations.stream().reduce((a, b) -> { - final double x = Math.min(a.getX(), b.getX()); - final double y = Math.min(a.getY(), b.getY()); - final double z = Math.min(a.getZ(), b.getZ()); - return new Location(a.getWorld(), x, y, z); - }).orElse(null); - - if (max == null || min == null || max.getWorld() == null) { - return; - } - - final List viewers = generator.getPlayers(); - - switch (Option.PARTICLE_SHAPE) { - case DOT -> { - final Location center = min.clone().add( - (max.getX() - min.getX()) / 2, - (max.getY() - min.getY()) / 2, - (max.getZ() - min.getZ()) / 2 - ); - viewers.forEach(p -> p.player.spawnParticle( - Option.PARTICLE_TYPE, center, DOT_PARTICLE_COUNT, DOT_PARTICLE_OFFSET, DOT_PARTICLE_OFFSET, DOT_PARTICLE_OFFSET, 0, - Option.PARTICLE_DATA - )); - } - case CIRCLE -> ParticleUtil.circle( - min.clone().add(CIRCLE_CENTER_OFFSET, CIRCLE_CENTER_OFFSET, CIRCLE_CENTER_OFFSET), Option.PARTICLE_TYPE, CIRCLE_RADIUS, 1 - ); - case BOX -> ParticleUtil.box( - org.bukkit.util.BoundingBox.of(min, max), max.getWorld(), Option.PARTICLE_TYPE, - viewers.isEmpty() ? null : viewers.get(0).player, BOX_PARTICLE_SPACING - ); - default -> {} - } - } - - /** - * Plays the configured sound at the first block's location. - */ - public void sound(List blocks) { - if (!generator.profile.get("sound").asBoolean() || blocks.isEmpty()) { - return; - } - - final Location loc = blocks.get(0).getLocation(); - generator.getPlayers().forEach(p -> - p.player.playSound(loc, Option.SOUND_TYPE, Option.SOUND_VOLUME, Option.SOUND_PITCH)); - generator.getSpectators().forEach(s -> - s.player.playSound(loc, Option.SOUND_TYPE, Option.SOUND_VOLUME, Option.SOUND_PITCH)); - } -} diff --git a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java index c81a240..0fe8035 100644 --- a/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java +++ b/src/main/java/dev/loki/loparkour/generator/jump/calculation/JumpValidator.java @@ -1,5 +1,6 @@ package dev.loki.loparkour.generator.jump.calculation; +import dev.loki.loparkour.config.options.Option; import dev.loki.loparkour.util.misc.MaterialUtil; import org.bukkit.Location; import org.bukkit.util.Vector; @@ -22,7 +23,12 @@ public class JumpValidator { private final double maxVerticalDown; public JumpValidator() { - this(MAX_JUMP_DISTANCE, MAX_HORIZONTAL_DISTANCE, MAX_VERTICAL_UP, MAX_VERTICAL_DOWN); + this( + Option.JUMP_VALIDATION_ENABLED ? Option.MAX_JUMP_DISTANCE : MAX_JUMP_DISTANCE, + Option.JUMP_VALIDATION_ENABLED ? Option.MAX_HORIZONTAL_DISTANCE : MAX_HORIZONTAL_DISTANCE, + Option.JUMP_VALIDATION_ENABLED ? Option.MAX_VERTICAL_UP : MAX_VERTICAL_UP, + Option.JUMP_VALIDATION_ENABLED ? Option.MAX_VERTICAL_DOWN : MAX_VERTICAL_DOWN + ); } public JumpValidator(double maxDistance, double maxHorizontal, double maxVerticalUp, double maxVerticalDown) { diff --git a/src/main/java/dev/loki/loparkour/generator/lifecycle/loop/LifecycleTickManager.java b/src/main/java/dev/loki/loparkour/generator/lifecycle/loop/LifecycleTickManager.java index 7e01e4f..a30eef6 100644 --- a/src/main/java/dev/loki/loparkour/generator/lifecycle/loop/LifecycleTickManager.java +++ b/src/main/java/dev/loki/loparkour/generator/lifecycle/loop/LifecycleTickManager.java @@ -93,12 +93,17 @@ public class LifecycleTickManager { private void maintainBlockLead() { final int currentBlocks = generator.state.generatedBlocks; - final int targetBlocks = generator.state.score + GENERATION_LEAD; - + final int targetBlocks = generator.state.score + generationLead(); + if (currentBlocks < targetBlocks) { final int needed = targetBlocks - currentBlocks; // Cap generation per tick to prevent runaway generation in multiplayer generator.generate(Math.min(needed, MAX_GENERATE_PER_TICK)); } } + + private int generationLead() { + final int lead = generator.profile.get("blockLead").asInt(); + return lead > 0 ? lead : GENERATION_LEAD; + } } diff --git a/src/main/java/dev/loki/loparkour/generator/lifecycle/player/GeneratorCleanup.java b/src/main/java/dev/loki/loparkour/generator/lifecycle/player/GeneratorCleanup.java index 96afe5f..499d123 100644 --- a/src/main/java/dev/loki/loparkour/generator/lifecycle/player/GeneratorCleanup.java +++ b/src/main/java/dev/loki/loparkour/generator/lifecycle/player/GeneratorCleanup.java @@ -9,7 +9,9 @@ import org.bukkit.block.Block; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Handles cleanup operations for parkour generator. @@ -91,26 +93,28 @@ public class GeneratorCleanup { } /** - * Remove trail blocks behind player at specified index. + * Remove schematic blocks once the player completes them. + * The end block is kept in history so the landing player can score it. */ - public void removeTrailBlocks(int startIndex) { - if (startIndex < 0 || startIndex >= generator.state.history.size()) { + public void removeSchematicBlocks() { + final List schematicBlocks = generator.state.schematicBlocks; + if (schematicBlocks == null || schematicBlocks.isEmpty()) { return; } - - final int endIndex = Math.min(startIndex + Option.BLOCK_CLEANUP_DISTANCE, generator.state.history.size()); - - for (int i = startIndex; i < endIndex; i++) { - final Block block = generator.state.history.get(i); - if (block != null && block.getType() != Material.AIR) { - block.setType(Material.AIR); + + final Set remove = new HashSet<>(); + for (int i = 0; i < schematicBlocks.size() - 1; i++) { + final Block block = schematicBlocks.get(i); + if (block != null) { + if (block.getType() != Material.AIR) { + block.setType(Material.AIR); + } + remove.add(block); } } - - // Remove from history (synchronized to prevent concurrent index shift) - synchronized (generator.state.history) { - generator.state.history.subList(startIndex, endIndex).clear(); - } + + generator.state.history.removeIf(remove::contains); + generator.state.schematicBlocks = null; } /** diff --git a/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java b/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java index 723d716..6980223 100644 --- a/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java +++ b/src/main/java/dev/loki/loparkour/generator/lifecycle/player/PlayerInteractionHandler.java @@ -112,7 +112,7 @@ public class PlayerInteractionHandler { private void handleBlockInteraction(@NotNull ParkourPlayer player, @NotNull Block block) { // Check if it's a schematic end block if (isSchematicEndBlock(block)) { - handleSchematicEndBlock(block); + handleSchematicEndBlock(); } else if (isOnSchematicBlock(block)) { // Schematic block that is NOT the last — skip scoring return; @@ -131,13 +131,9 @@ public class PlayerInteractionHandler { && historyContains(generator.state.schematicBlocks, block); } - private void handleSchematicEndBlock(@NotNull Block block) { - // Remove schematic blocks and continue with normal generation - if (generator.state.schematicBlocks != null) { - cleanup.removeTrailBlocks(0); // Remove schematic trail - generator.state.schematicBlocks = null; - } - + private void handleSchematicEndBlock() { + cleanup.removeSchematicBlocks(); + // Continue normal generation generator.generate(GENERATION_LEAD); } diff --git a/src/main/java/dev/loki/loparkour/mode/impl/multi/CoopGenerator.java b/src/main/java/dev/loki/loparkour/mode/impl/multi/CoopGenerator.java new file mode 100644 index 0000000..99bc091 --- /dev/null +++ b/src/main/java/dev/loki/loparkour/mode/impl/multi/CoopGenerator.java @@ -0,0 +1,89 @@ +package dev.loki.loparkour.mode.impl.multi; + +import dev.loki.loparkour.config.locale.Locales; +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 dev.loki.loparkour.util.text.ColorUtil; +import org.bukkit.Sound; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Co-op generator — all players share one score; per-UUID block + * contributions are tracked for the fall() stats message. + */ +class CoopGenerator extends ParkourGenerator { + + private static final int MILESTONE_INTERVAL = 50; + private static final int TITLE_FADE_IN = 5; + private static final int TITLE_STAY = 40; + private static final int TITLE_FADE_OUT = 10; + private static final float MILESTONE_VOLUME = 0.6f; + private static final float MILESTONE_PITCH = 1.5f; + private static final int PERCENT_MULTIPLIER = 100; + private final Map contributions = new HashMap<>(); + + CoopGenerator(@NotNull Session session) { + super(session); + session.getPlayers().forEach(pp -> contributions.put(pp.getUUID(), 0)); + } + + @Override + protected void score() { + super.score(); + + for (final ParkourPlayer pp : getPlayers()) { + contributions.compute(pp.getUUID(), (uuid, count) -> (count == null ? 0 : count) + 1); + } + + if (state.score % MILESTONE_INTERVAL == 0) { + getPlayers().forEach(pp -> { + pp.player.sendTitle( + ColorUtil.color(Locales.getString(pp.locale, "modes.coop.milestone_title").formatted(state.score)), + ColorUtil.color(Locales.getString(pp.locale, "modes.coop.milestone_subtitle")), + TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT); + pp.player.playSound(pp.getLocation(), + Sound.ENTITY_PLAYER_LEVELUP, MILESTONE_VOLUME, MILESTONE_PITCH); + }); + } + } + + /** + * Registers a player joining mid-game so their blocks count in fall() stats. + */ + void onPlayerJoin(ParkourPlayer pp) { + contributions.putIfAbsent(pp.getUUID(), 0); + } + + @Override + public void fall() { + getPlayers().forEach(pp -> { + final int contrib = contributions.getOrDefault(pp.getUUID(), 0); + final int pct = state.score > 0 ? (contrib * PERCENT_MULTIPLIER / state.score) : 0; + pp.sendTranslated("modes.coop.stats", + Integer.toString(state.score), + Integer.toString(contrib), + Integer.toString(pct)); + }); + + super.fall(); + } + + @Override + public void reset(boolean regenerate) { + super.reset(regenerate); + contributions.clear(); + getPlayers().forEach(pp -> contributions.put(pp.getUUID(), 0)); + } + + @Override + public Mode getMode() { + return Modes.COOP; + } +} diff --git a/src/main/java/dev/loki/loparkour/mode/impl/multi/CoopMode.java b/src/main/java/dev/loki/loparkour/mode/impl/multi/CoopMode.java index 4779ced..e72f9b9 100644 --- a/src/main/java/dev/loki/loparkour/mode/impl/multi/CoopMode.java +++ b/src/main/java/dev/loki/loparkour/mode/impl/multi/CoopMode.java @@ -1,44 +1,23 @@ package dev.loki.loparkour.mode.impl.multi; -import dev.loki.loparkour.mode.base.MultiMode; -import dev.loki.loparkour.mode.base.ModeMessages; -import dev.loki.loparkour.mode.base.Mode; -import dev.loki.loparkour.mode.base.Modes; - import dev.loki.loparkour.config.core.Config; -import dev.loki.loparkour.config.locale.Locales; -import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator; -import dev.loki.loparkour.util.text.ColorUtil; import dev.loki.loparkour.leaderboard.core.Leaderboard; import dev.loki.loparkour.menu.core.Menus; +import dev.loki.loparkour.mode.base.ModeMessages; +import dev.loki.loparkour.mode.base.MultiMode; import dev.loki.loparkour.player.core.ParkourPlayer; import dev.loki.loparkour.player.core.ParkourUser; import dev.loki.loparkour.session.core.Session; -import org.bukkit.Sound; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - /** * Co-op Mode — multiple players share one session and one score. * *

Implements {@link MultiMode} so it is excluded from the single-player * mode selection menu in {@link dev.loki.loparkour.menu.play.SingleMenu}. * Players join via /parkour coop or through a lobby invite flow.

- * - *

Problems fixed vs previous version: - *

    - *
  • Did NOT implement MultiMode → showed up in single-player menu
  • - *
  • CoopGenerator was a private inner class — inaccessible from join/leave
  • - *
  • sharedScore was a separate field that duplicated state.score
  • - *
  • fall() called super.fall() THEN tried to clear state that was already reset
  • - *
  • playerContributions never cleared between falls correctly
  • - *
  • create(Player) silently created a solo session instead of rejecting
  • - *
*/ public class CoopMode implements MultiMode { @@ -50,10 +29,6 @@ public class CoopMode implements MultiMode { return "coop"; } - /** - * Returns null so this mode never appears in any menu item list. - * Players join via lobby invite or command only. - */ @Override @Nullable public dev.loki.loparkour.util.item.Item getItem(String locale) { @@ -67,23 +42,28 @@ public class CoopMode implements MultiMode { } /** - * Called when a player tries to start coop solo from a command/menu. - * Opens the lobby menu so they can invite others — does NOT create a session here. - * A session is created only once a second player joins via {@link #join}. + * Creates a coop session with the caller as host and opens the lobby so + * the host can invite others. Later joiners are added via {@link #join}. */ @Override public void create(Player player) { if (!ModeMessages.checkJoiningEnabled(player)) { return; } - // Opening lobby menu lets player invite others — actual session created in join() + + final ParkourPlayer pp = ParkourPlayer.getPlayer(player); + if (pp != null && pp.session.generator instanceof CoopGenerator) { + return; + } + + player.closeInventory(); + Session.create(CoopGenerator::new, + session -> session.getPlayers().size() < getMaxPlayers(), + null, + player); Menus.LOBBY.open(player); } - /** - * Adds a player to an existing coop session, or creates a new session if - * the target player is not yet in one. - */ @Override public void join(Player joiningPlayer, Session session) { if (!ModeMessages.checkJoiningEnabled(joiningPlayer)) { @@ -94,20 +74,17 @@ public class CoopMode implements MultiMode { return; } - // Register the joining player into the existing session final ParkourPlayer pp = ParkourUser.register(joiningPlayer, session); session.addPlayers(pp); pp.updateGeneratorSettings(session.generator); + if (session.generator instanceof CoopGenerator coopGenerator) { + coopGenerator.onPlayerJoin(pp); + } - // Inform everyone session.getPlayers().forEach(p -> p.sendTranslated("modes.coop.player_joined", joiningPlayer.getName())); } - /** - * Removes a player from a coop session. - * If the last player leaves, the session ends normally. - */ @Override public void leave(Player player, Session session) { final ParkourPlayer pp = ParkourPlayer.getPlayer(player); @@ -121,79 +98,4 @@ public class CoopMode implements MultiMode { public int getMaxPlayers() { return Config.CONFIG.getInt("modes.coop.max-players"); } - - /** Package-accessible so Session.create() lambda and join() can use it. */ - static class CoopGenerator extends ParkourGenerator { - - private static final int MILESTONE_INTERVAL = 50; - private static final int TITLE_FADE_IN = 5; - private static final int TITLE_STAY = 40; - private static final int TITLE_FADE_OUT = 10; - private static final float MILESTONE_VOLUME = 0.6f; - private static final float MILESTONE_PITCH = 1.5f; - private static final int PERCENT_MULTIPLIER = 100; - /** Individual block contributions per player UUID. */ - private final Map contributions = new HashMap<>(); - - CoopGenerator(@NotNull Session session) { - super(session); - session.getPlayers().forEach(pp -> contributions.put(pp.getUUID(), 0)); - } - - @Override - protected void score() { - super.score(); // increments state.score and totalScore - - // Track contributions for all players in session - for (final ParkourPlayer pp : getPlayers()) { - contributions.compute(pp.getUUID(), (uuid, count) -> (count == null ? 0 : count) + 1); - } - - // Milestone every 50 points - if (state.score % MILESTONE_INTERVAL == 0) { - getPlayers().forEach(pp -> { - pp.player.sendTitle( - ColorUtil.color(Locales.getString(pp.locale, "modes.coop.milestone_title").formatted(state.score)), - ColorUtil.color(Locales.getString(pp.locale, "modes.coop.milestone_subtitle")), - TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT); - pp.player.playSound(pp.getLocation(), - Sound.ENTITY_PLAYER_LEVELUP, MILESTONE_VOLUME, MILESTONE_PITCH); - }); - } - } - - /** - * When a new player joins mid-game, add them to contributions map. - */ - public void onPlayerJoin(ParkourPlayer pp) { - contributions.putIfAbsent(pp.getUUID(), 0); - } - - @Override - public void fall() { - // Show each player their individual contribution before reset - getPlayers().forEach(pp -> { - final int contrib = contributions.getOrDefault(pp.getUUID(), 0); - final int pct = state.score > 0 ? (contrib * PERCENT_MULTIPLIER / state.score) : 0; - pp.sendTranslated("modes.coop.stats", - Integer.toString(state.score), - Integer.toString(contrib), - Integer.toString(pct)); - }); - - super.fall(); // handles reset, leaderboard, etc. - } - - @Override - public void reset(boolean regenerate) { - super.reset(regenerate); - contributions.clear(); - getPlayers().forEach(pp -> contributions.put(pp.getUUID(), 0)); - } - - @Override - public Mode getMode() { - return Modes.COOP; - } - } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 1b223be..a0a8240 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -150,26 +150,6 @@ jump-validation: max-vertical-up: 1.25 max-vertical-down: 3.0 -# -= Jump types options =- -jump-types: - enabled: false - types: - neo-jump: - enabled: true - chance: 0.05 - head-hitter: - enabled: true - chance: 0.08 - fence-jump: - enabled: true - chance: 0.10 - trapdoor-jump: - enabled: true - chance: 0.12 - ladder-jump: - enabled: true - chance: 0.07 - # -= Memory optimization =- memory: block-cleanup-distance: 20 diff --git a/src/main/resources/generation.yml b/src/main/resources/generation.yml index 43399cf..dc44ec5 100644 --- a/src/main/resources/generation.yml +++ b/src/main/resources/generation.yml @@ -34,17 +34,6 @@ generation: pane: 10 fence: 10 - # -= Jump type variations =- - # Chances for different jump types within normal blocks - # These add variety to the parkour generation - jump-types: - normal: 70 - neo: 5 - head-hitter: 5 - fence: 10 - trapdoor: 5 - ladder: 5 - # -= Jump settings =- settings: