diff --git a/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/QueueService.java b/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/QueueService.java index 58ed384f..e0d5d30e 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/QueueService.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/QueueService.java @@ -12,7 +12,11 @@ import dev.lrxh.neptune.profile.data.ProfileState; import dev.lrxh.neptune.profile.impl.Profile; import dev.lrxh.neptune.providers.clickable.Replacement; +import dev.lrxh.neptune.utils.CC; import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import net.kyori.adventure.title.Title; +import java.time.Duration; import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; @@ -51,6 +55,15 @@ public void add(QueueEntry queueEntry, boolean add) { MessagesLocale.QUEUE_JOIN.send(playerUUID, new Replacement("", kit.getDisplayName()), new Replacement("", String.valueOf(profile.getSettingData().getMaxPing()))); + + // One-shot title: Joined Queue + Bukkit.getScheduler().runTask(Neptune.get(), () -> { + Player p = Bukkit.getPlayer(playerUUID); + if (p != null && p.isOnline()) { + Title.Times times = Title.Times.times(Duration.ZERO, Duration.ofMillis(1200), Duration.ZERO); + p.showTitle(Title.title(CC.color("&eJoined Queue"), CC.color("&7Searching for opponent..."), times)); + } + }); } } diff --git a/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/tasks/QueueCheckTask.java b/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/tasks/QueueCheckTask.java index 2febaa18..943b3758 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/tasks/QueueCheckTask.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/feature/queue/tasks/QueueCheckTask.java @@ -16,9 +16,17 @@ import dev.lrxh.neptune.utils.CC; import dev.lrxh.neptune.utils.PlayerUtil; import dev.lrxh.neptune.utils.tasks.NeptuneRunnable; -import dev.lrxh.neptune.utils.tasks.TaskScheduler; +import net.kyori.adventure.title.Title; +import org.bukkit.Sound; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import dev.lrxh.neptune.Neptune; +import java.time.Duration; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.Location; +import org.bukkit.World; +import dev.lrxh.neptune.game.arena.VirtualArena; import java.util.Map; import java.util.Queue; @@ -83,42 +91,105 @@ public void run() { } kit.getRandomArena().thenAccept(arena -> { - if (arena == null) { - PlayerUtil.sendMessage(uuid1, CC.error("No valid arena was found for this kit!")); - PlayerUtil.sendMessage(uuid2, CC.error("No valid arena was found for this kit!")); - return; - } + Bukkit.getScheduler().runTask(Neptune.get(), () -> { + if (arena == null) { + PlayerUtil.sendMessage(uuid1, CC.error("No valid arena was found for this kit!")); + PlayerUtil.sendMessage(uuid2, CC.error("No valid arena was found for this kit!")); + return; + } + + // Re-fetch players on main thread and validate online + Player p1 = Bukkit.getPlayer(uuid1); + Player p2 = Bukkit.getPlayer(uuid2); + if (p1 == null || p2 == null || !p1.isOnline() || !p2.isOnline()) { + return; + } - Participant participant1 = new Participant(player1); - Participant participant2 = new Participant(player2); - - MessagesLocale.MATCH_FOUND.send(uuid1, - new Replacement("", participant2.getNameUnColored()), - new Replacement("", kit.getDisplayName()), - new Replacement("", arena.getDisplayName()), - new Replacement("", String.valueOf(ping2)), - new Replacement("", String.valueOf(profile2.getGameData().get(kit).getElo())), - new Replacement("", String.valueOf(profile1.getGameData().get(kit).getElo())), - new Replacement("", String.valueOf(ping1))); - - MessagesLocale.MATCH_FOUND.send(uuid2, - new Replacement("", participant1.getNameUnColored()), - new Replacement("", kit.getDisplayName()), - new Replacement("", arena.getDisplayName()), - new Replacement("", String.valueOf(ping1)), - new Replacement("", String.valueOf(profile1.getGameData().get(kit).getElo())), - new Replacement("", String.valueOf(profile2.getGameData().get(kit).getElo())), - new Replacement("", String.valueOf(ping2))); - - TaskScheduler.get().startTaskCurrentTick(new NeptuneRunnable() { - @Override - public void run() { + Participant participant1 = new Participant(p1); + Participant participant2 = new Participant(p2); + + MessagesLocale.MATCH_FOUND.send(uuid1, + new Replacement("", participant2.getNameUnColored()), + new Replacement("", kit.getDisplayName()), + new Replacement("", arena.getDisplayName()), + new Replacement("", String.valueOf(ping2)), + new Replacement("", String.valueOf(profile2.getGameData().get(kit).getElo())), + new Replacement("", String.valueOf(profile1.getGameData().get(kit).getElo())), + new Replacement("", String.valueOf(ping1))); + + MessagesLocale.MATCH_FOUND.send(uuid2, + new Replacement("", participant1.getNameUnColored()), + new Replacement("", kit.getDisplayName()), + new Replacement("", arena.getDisplayName()), + new Replacement("", String.valueOf(ping1)), + new Replacement("", String.valueOf(profile1.getGameData().get(kit).getElo())), + new Replacement("", String.valueOf(profile2.getGameData().get(kit).getElo())), + new Replacement("", String.valueOf(ping2))); + + // Preload arena chunks so teleport feels instant after delay + preloadArena(arena); + + // Prepare players: close inventory, brief blindness, play accept sound, animate titles + p1.closeInventory(); + p2.closeInventory(); + + p1.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 40, 1)); + p2.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 40, 1)); + + Sound sound = Sound.ITEM_MACE_SMASH_AIR; + p1.playSound(p1.getLocation(), sound, 1.0f, 1.0f); + p2.playSound(p2.getLocation(), sound, 1.0f, 1.0f); + + animateTitles(p1, p2, 100L); + + // Delay start to allow arena generation to finalize + Bukkit.getScheduler().runTaskLater(Neptune.get(), () -> { MatchService.get().startMatch(participant1, participant2, kit, arena, false, kit.is(KitRule.BEST_OF_THREE) ? 3 : 1); - } + }, 100L); }); }); } } + private void animateTitles(Player p1, Player p2, long delayTicks) { + String[] titles = { + "&e⚔ &6Match Found &e⚔", + "&6⚔ &eMatch Found &6⚔", + "&e⚔ &6Match Found &e⚔", + "&6⚔ &eMatch Found &6⚔" + }; + + String[] subs = { + "&7Teleporting to arena.", + "&7Teleporting to arena..", + "&7Teleporting to arena...", + "&7Teleporting to arena." + }; + for (long t = 1L; t < delayTicks; t += 5L) { + int idx = (int) ((t / 5L) % titles.length); + Bukkit.getScheduler().runTaskLater(Neptune.get(), () -> { + Title.Times times = Title.Times.times(Duration.ZERO, Duration.ofMillis(500), Duration.ZERO); + p1.showTitle(Title.title(CC.color(titles[idx]), CC.color(subs[idx]), times)); + p2.showTitle(Title.title(CC.color(titles[idx]), CC.color(subs[idx]), times)); + }, t); + } + } + + private void preloadArena(VirtualArena arena) { + if (arena == null) return; + Location[] important = new Location[]{arena.getRedSpawn(), arena.getBlueSpawn(), arena.getMin(), arena.getMax()}; + for (Location loc : important) { + if (loc == null) continue; + World world = loc.getWorld(); + if (world == null) continue; + int baseCx = loc.getBlockX() >> 4; + int baseCz = loc.getBlockZ() >> 4; + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + world.getChunkAt(baseCx + dx, baseCz + dz).load(true); + } + } + } + } } diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/duel/DuelRequest.java b/Plugin/src/main/java/dev/lrxh/neptune/game/duel/DuelRequest.java index dce0e666..f73ee010 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/duel/DuelRequest.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/duel/DuelRequest.java @@ -12,7 +12,12 @@ import dev.lrxh.neptune.utils.CC; import lombok.Getter; import org.bukkit.Bukkit; +import org.bukkit.Sound; import org.bukkit.entity.Player; +import net.kyori.adventure.title.Title; +import java.time.Duration; +import org.bukkit.Location; +import org.bukkit.World; import java.util.ArrayList; import java.util.List; @@ -33,6 +38,23 @@ public DuelRequest(UUID sender, Kit kit, VirtualArena arena, boolean party, int this.rounds = rounds; } + private void preloadArena(dev.lrxh.neptune.game.arena.VirtualArena arena) { + if (arena == null) return; + Location[] important = new Location[]{arena.getRedSpawn(), arena.getBlueSpawn(), arena.getMin(), arena.getMax()}; + for (Location loc : important) { + if (loc == null) continue; + World world = loc.getWorld(); + if (world == null) continue; + int baseCx = loc.getBlockX() >> 4; + int baseCz = loc.getBlockZ() >> 4; + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + world.getChunkAt(baseCx + dx, baseCz + dz).load(true); + } + } + } + } + public void start(UUID receiver) { if (party) { partyDuel(receiver); @@ -61,6 +83,7 @@ public void partyDuel(UUID receiver) { Profile senderProfile = API.getProfile(getSender()); List participants = new ArrayList<>(); + List players = new ArrayList<>(); List teamAList = new ArrayList<>(); @@ -72,6 +95,7 @@ public void partyDuel(UUID receiver) { Participant participant = new Participant(player); teamAList.add(participant); participants.add(participant); + players.add(player); } List teamBList = new ArrayList<>(); @@ -84,6 +108,7 @@ public void partyDuel(UUID receiver) { Participant participant = new Participant(player); teamBList.add(participant); participants.add(participant); + players.add(player); } MatchTeam teamA = new MatchTeam(teamAList); @@ -108,8 +133,45 @@ public void partyDuel(UUID receiver) { return; } - Bukkit.getScheduler().runTask(Neptune.get(), () -> { + // Preload arena chunks so teleport feels instant after delay + preloadArena(arena); + + // Play accept sound, close inventories, and show titles to all party members + Sound sound = Sound.ITEM_MACE_SMASH_AIR; + for (Player p : players) { + p.playSound(p.getLocation(), sound, 1.0f, 1.0f); + p.closeInventory(); + } + + partyDuelTitles(players, 100L); + + Bukkit.getScheduler().runTaskLater(Neptune.get(), () -> { MatchService.get().startMatch(teamA, teamB, kit, arena); - }); + }, 100L); + } + + private void partyDuelTitles(List players, long delayTicks) { + String[] titles = { + "&e⚔ &6Duel Accepted &e⚔", + "&6⚔ &6Duel Accepted &6⚔", + "&e⚔ &6Duel Accepted &e⚔", + "&6⚔ &6Duel Accepted &6⚔" + }; + + String[] subs = { + "&7Teleporting to arena.", + "&7Teleporting to arena..", + "&7Teleporting to arena...", + "&7Teleporting to arena." + }; + for (long t = 1L; t < delayTicks; t += 5L) { + int idx = (int) ((t / 5L) % titles.length); + Bukkit.getScheduler().runTaskLater(Neptune.get(), () -> { + Title.Times times = Title.Times.times(Duration.ZERO, Duration.ofMillis(500), Duration.ZERO); + for (Player p : players) { + p.showTitle(Title.title(CC.color(titles[idx]), CC.color(subs[idx]), times)); + } + }, t); + } } } diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/kit/Kit.java b/Plugin/src/main/java/dev/lrxh/neptune/game/kit/Kit.java index c6b81eee..14a16eb0 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/kit/Kit.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/kit/Kit.java @@ -24,6 +24,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -223,7 +224,18 @@ public void giveLoadout(UUID playerUUID) { player.getInventory().setContents(gameData.get(this).getKitLoadout().toArray(new ItemStack[0])); } - player.addPotionEffects(potionEffects); + // Ensure no invisibility persists from kit or prior state + player.removePotionEffect(PotionEffectType.INVISIBILITY); + player.setInvisible(false); + + // Apply kit potion effects excluding invisibility + List effects = new ArrayList<>(); + for (PotionEffect effect : potionEffects) { + if (effect != null && effect.getType() != PotionEffectType.INVISIBILITY) { + effects.add(effect); + } + } + player.addPotionEffects(effects); player.updateInventory(); } @@ -244,7 +256,18 @@ public void giveLoadout(Participant participant) { participant.getColor().getContentColor())); } - player.addPotionEffects(potionEffects); + // Ensure no invisibility persists from kit or prior state + player.removePotionEffect(PotionEffectType.INVISIBILITY); + player.setInvisible(false); + + // Apply kit potion effects excluding invisibility + List effects2 = new ArrayList<>(); + for (PotionEffect effect : potionEffects) { + if (effect != null && effect.getType() != PotionEffectType.INVISIBILITY) { + effects2.add(effect); + } + } + player.addPotionEffects(effects2); player.updateInventory(); } diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/match/Match.java b/Plugin/src/main/java/dev/lrxh/neptune/game/match/Match.java index 1a73f1e0..36349356 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/match/Match.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/match/Match.java @@ -343,6 +343,7 @@ public void checkRules() { participant.setDead(false); }); + resetVisibilityInMatch(); showPlayerForSpectators(); } @@ -358,7 +359,9 @@ public void hideHealth() { public void hideParticipant(Participant participant) { forEachParticipant(p -> { if (!p.equals(participant)) { - p.getPlayer().hidePlayer(Neptune.get(), participant.getPlayer()); + if (p.getPlayer() != null && participant.getPlayer() != null) { + p.getPlayer().hidePlayer(Neptune.get(), participant.getPlayer()); + } } }); } @@ -366,11 +369,23 @@ public void hideParticipant(Participant participant) { public void showParticipant(Participant participant) { forEachParticipant(p -> { if (!p.equals(participant)) { - p.getPlayer().showPlayer(Neptune.get(), participant.getPlayer()); + if (p.getPlayer() != null && participant.getPlayer() != null) { + p.getPlayer().showPlayer(Neptune.get(), participant.getPlayer()); + } } }); } + public void resetVisibilityInMatch() { + forEachParticipant(a -> forEachParticipant(b -> { + if (!a.equals(b)) { + if (a.getPlayer() != null && b.getPlayer() != null) { + a.getPlayer().showPlayer(Neptune.get(), b.getPlayer()); + } + } + })); + } + private void showHealth() { forEachPlayer(player -> { Objective objective = player.getScoreboard().getObjective(DisplaySlot.BELOW_NAME); @@ -403,6 +418,7 @@ public void setupParticipants() { par.reset(); showParticipant(par); }); + resetVisibilityInMatch(); } public void sendDeathMessage(Participant deadParticipant) { diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/ffa/FfaFightMatch.java b/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/ffa/FfaFightMatch.java index c13cb807..0737a562 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/ffa/FfaFightMatch.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/ffa/FfaFightMatch.java @@ -135,6 +135,7 @@ public void onLeave(Participant participant, boolean quit) { public void startMatch() { setState(MatchState.IN_ROUND); showPlayerForSpectators(); + resetVisibilityInMatch(); playSound(Sound.ENTITY_FIREWORK_ROCKET_BLAST); sendTitle(CC.color(MessagesLocale.MATCH_START_TITLE_HEADER.getString()), CC.color(MessagesLocale.MATCH_START_TITLE_FOOTER.getString()), 20); } diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/solo/SoloFightMatch.java b/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/solo/SoloFightMatch.java index ff6332a5..b7c6b1c0 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/solo/SoloFightMatch.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/solo/SoloFightMatch.java @@ -77,6 +77,9 @@ public void end(Participant loser) { MessagesLocale.MATCH_YOU.getString())), 100); + // Winner celebratory animation and sound + animateWinnerTitles(winner); + if (!loser.isLeft() && !loser.isDisconnected()) loser.sendTitle(CC.color(MessagesLocale.MATCH_LOSER_TITLE_HEADER.getString()), CC.color(MessagesLocale.MATCH_LOSER_TITLE_FOOTER.getString().replace("", @@ -293,11 +296,37 @@ public void onLeave(Participant participant, boolean quit) { public void startMatch() { setState(MatchState.IN_ROUND); showPlayerForSpectators(); + resetVisibilityInMatch(); playSound(Sound.ENTITY_FIREWORK_ROCKET_BLAST); sendTitle(CC.color(MessagesLocale.MATCH_START_TITLE_HEADER.getString()), CC.color(MessagesLocale.MATCH_START_TITLE_FOOTER.getString()), 20); } + private void animateWinnerTitles(Participant winner) { + String[] frames = { + "&e&l🏆 &6&lVICTORY &e&l🏆", + "&6&l🏆 &e&lVICTORY &6&l🏆", + "&e&l🏆 &6&lVICTORY &e&l🏆", + "&6&l🏆 &e&lVICTORY &6&l🏆", + "&e&l🏆 &6&lVICTORY &e&l🏆", + "&6&l🏆 &e&lVICTORY &6&l🏆", + "&e&l🏆 &6&lVICTORY &e&l🏆", + "&6&l🏆 &e&lVICTORY &6&l🏆" + }; + + if (winner.getPlayer() == null) return; + + // Play activation sound once at start + winner.getPlayer().playSound(winner.getPlayer().getLocation(), Sound.BLOCK_BEACON_ACTIVATE, 1.0f, 1.0f); + + for (int i = 0; i < frames.length; i++) { + int idx = i; + Bukkit.getScheduler().runTaskLater(dev.lrxh.neptune.Neptune.get(), () -> { + winner.sendTitle(CC.color(frames[idx]), CC.color(" "), 6); + }, 1 + (idx * 5L)); + } + } + @Override public List getParticipants() { return List.of(participantA, participantB); diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/team/TeamFightMatch.java b/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/team/TeamFightMatch.java index afc285d9..e1df67d0 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/team/TeamFightMatch.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/match/impl/team/TeamFightMatch.java @@ -182,6 +182,7 @@ public void onLeave(Participant participant, boolean quit) { public void startMatch() { setState(MatchState.IN_ROUND); showPlayerForSpectators(); + resetVisibilityInMatch(); playSound(Sound.ENTITY_FIREWORK_ROCKET_BLAST); sendTitle(CC.color(MessagesLocale.MATCH_START_TITLE_FOOTER.getString()), CC.color(MessagesLocale.MATCH_START_TITLE_FOOTER.getString()), 10); } diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/match/listener/MatchListener.java b/Plugin/src/main/java/dev/lrxh/neptune/game/match/listener/MatchListener.java index 69fa0eea..8a7fd081 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/match/listener/MatchListener.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/match/listener/MatchListener.java @@ -37,6 +37,9 @@ import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.event.vehicle.VehicleEnterEvent; import org.bukkit.event.vehicle.VehicleExitEvent; import org.bukkit.inventory.ItemStack; @@ -385,7 +388,18 @@ public void onEntityPush(EntityPushedByEntityAttackEvent event) { } if (!target.canSee(shooter) || !shooter.canSee(target)) { - event.setCancelled(true); + // Attempt to restore visibility if both are in matches (most likely same match here) + Optional shooterProfile = getProfile(shooter); + Optional targetProfile = getProfile(target); + if (shooterProfile.isPresent() && targetProfile.isPresent()) { + shooter.showPlayer(Neptune.get(), target); + target.showPlayer(Neptune.get(), shooter); + if (!target.canSee(shooter) || !shooter.canSee(target)) { + event.setCancelled(true); + } + } else { + event.setCancelled(true); + } } if (event.getPushedBy() instanceof Player attacker) { @@ -399,7 +413,11 @@ public void onEntityPush(EntityPushedByEntityAttackEvent event) { } if (!victim.canSee(attacker) || !attacker.canSee(victim)) { - event.setCancelled(true); + attacker.showPlayer(Neptune.get(), victim); + victim.showPlayer(Neptune.get(), attacker); + if (!victim.canSee(attacker) || !attacker.canSee(victim)) { + event.setCancelled(true); + } } } } @@ -580,6 +598,12 @@ public void onEntityDamageByEntityMonitor(EntityDamageByEntityEvent event) { return; } + // Ensure both players can see each other during combat interactions + if (!attacker.canSee(player) || !player.canSee(attacker)) { + attacker.showPlayer(Neptune.get(), player); + player.showPlayer(Neptune.get(), attacker); + } + if (match.getParticipant(attacker).isDead()) { event.setCancelled(true); } @@ -1060,4 +1084,30 @@ private Optional getMatchForPlayer(Player player) { .filter(this::isPlayerInMatch) .map(Profile::getMatch); } + + @EventHandler + public void onTeleport(PlayerTeleportEvent event) { + Player player = event.getPlayer(); + Optional matchOpt = getMatchForPlayer(player); + if (matchOpt.isEmpty()) return; + + Bukkit.getScheduler().runTaskLater(Neptune.get(), () -> { + Match match = matchOpt.get(); + match.resetVisibilityInMatch(); + match.showPlayerForSpectators(); + }, 2L); + } + + @EventHandler + public void onWorldChange(PlayerChangedWorldEvent event) { + Player player = event.getPlayer(); + Optional matchOpt = getMatchForPlayer(player); + if (matchOpt.isEmpty()) return; + + Bukkit.getScheduler().runTaskLater(Neptune.get(), () -> { + Match match = matchOpt.get(); + match.resetVisibilityInMatch(); + match.showPlayerForSpectators(); + }, 2L); + } } \ No newline at end of file diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchRespawnRunnable.java b/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchRespawnRunnable.java index 2677cccd..bfd6613f 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchRespawnRunnable.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchRespawnRunnable.java @@ -56,6 +56,7 @@ public void run() { match.setupPlayer(participant.getPlayerUUID()); participant.setDead(false); match.showParticipant(participant); + match.resetVisibilityInMatch(); participant.sendMessage(MessagesLocale.MATCH_RESPAWNED); stop(); MatchParticipantRespawnEvent event = new MatchParticipantRespawnEvent(match, participant); diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchStartRunnable.java b/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchStartRunnable.java index c6c77d87..f5ba02b2 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchStartRunnable.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/match/tasks/MatchStartRunnable.java @@ -47,6 +47,10 @@ public void run() { match.sendMessage(MessagesLocale.MATCH_STARTED); match.startMatch(); match.checkRules(); + Bukkit.getScheduler().runTaskLater(dev.lrxh.neptune.Neptune.get(), () -> { + match.resetVisibilityInMatch(); + match.showPlayerForSpectators(); + }, 5L); checkFollowings(); match.getTime().setStop(false); diff --git a/Plugin/src/main/java/dev/lrxh/neptune/profile/data/Visibility.java b/Plugin/src/main/java/dev/lrxh/neptune/profile/data/Visibility.java index af100e3f..3e68e066 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/profile/data/Visibility.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/profile/data/Visibility.java @@ -32,6 +32,14 @@ public void handle(UUID otherUUID) { Profile viewerProfile = API.getProfile(uuid); Profile otherProfile = API.getProfile(otherUUID); + // If either player is in a match, force mutual visibility between them. + // This prevents lobby visibility preferences from hiding opponents at match start. + if (viewerProfile.hasState(ProfileState.IN_GAME) || otherProfile.hasState(ProfileState.IN_GAME)) { + viewerPlayer.showPlayer(Neptune.get(), otherPlayer); + otherPlayer.showPlayer(Neptune.get(), viewerPlayer); + return; + } + if (viewerProfile.hasState(ProfileState.IN_LOBBY)) { if (!viewerProfile.getSettingData().isPlayerVisibility()) { viewerPlayer.hidePlayer(Neptune.get(), otherPlayer);