All currently known minstrels
+ */ + public ListThe triggered event
- */ @EventHandler - public void playerJoinListener(PlayerJoinEvent event) { - for (NPC npc : CitizensAPI.getNPCRegistry()) { - if (npc.hasTrait(MinstrelTrait.class)) { - MinstrelTrait minstrelTrait = npc.getTraitNullable(MinstrelTrait.class); - minstrelTrait.getPlaylist().play(minstrelTrait, event.getPlayer()); + public void playerMoveListener(PlayerMoveEvent event) { + if (event.getTo() == null || event.getFrom().getBlock().equals(event.getTo().getBlock())) { + return; + } + + for (MinstrelTrait minstrelTrait : MinstrelPlugin.getInstance().getMinstrels()) { + if (!SoundHelper.canHear(event.getPlayer(), minstrelTrait) || + QueueManager.isQueued(event.getPlayer(), minstrelTrait)) { + continue; } + + QueueManager.queue(new SongEndTime(event.getPlayer(), minstrelTrait, 0)); } } diff --git a/src/main/java/net/knarcraft/minstrel/manager/QueueManager.java b/src/main/java/net/knarcraft/minstrel/manager/QueueManager.java new file mode 100644 index 0000000..aff3ec3 --- /dev/null +++ b/src/main/java/net/knarcraft/minstrel/manager/QueueManager.java @@ -0,0 +1,66 @@ +package net.knarcraft.minstrel.manager; + +import net.knarcraft.minstrel.music.SongEndTime; +import net.knarcraft.minstrel.trait.MinstrelTrait; +import net.knarcraft.minstrel.util.SoundHelper; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; + +/** + * A manager keeping track of the queue for playing the next song + */ +public class QueueManager { + + private static final QueueThe player to check
+ * @param minstrelTraitThe minstrel to check
+ * @returnTrue if already queued
+ */ + public static boolean isQueued(Player player, MinstrelTrait minstrelTrait) { + return queuedMinstrels.get(player) != null && queuedMinstrels.get(player).contains(minstrelTrait); + } + + /** + * Queues the given song end time + * + * @param songEndTimeThe song end time to queue
+ */ + public static void queue(SongEndTime songEndTime) { + songEndTimes.add(songEndTime); + queuedMinstrels.computeIfAbsent(songEndTime.player(), k -> new HashSet<>()); + queuedMinstrels.get(songEndTime.player()).add(songEndTime.minstrelTrait()); + } + +} diff --git a/src/main/java/net/knarcraft/minstrel/music/Playlist.java b/src/main/java/net/knarcraft/minstrel/music/Playlist.java index 8faa057..a0dbffb 100644 --- a/src/main/java/net/knarcraft/minstrel/music/Playlist.java +++ b/src/main/java/net/knarcraft/minstrel/music/Playlist.java @@ -1,6 +1,7 @@ package net.knarcraft.minstrel.music; import net.knarcraft.minstrel.MinstrelPlugin; +import net.knarcraft.minstrel.manager.QueueManager; import net.knarcraft.minstrel.trait.MinstrelTrait; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -16,20 +17,16 @@ import java.util.Map; public class Playlist { private final ListThe songs contained in this playlist
- * @param loopWhether to loop around when this playlist finishes
*/ - public Playlist(ListThe minstrel to play this playlist for
- */ - public void play(MinstrelTrait trait) { - //If this playlist is empty, do nothing - if (this.songs.size() < 1) { - return; - } - - if (this.currentlyPlaying >= songs.size()) { - if (this.loop) { - this.currentlyPlaying = 0; - } else { - return; - } - } - - //Prevent overlapping music if a song has been played outside the global cycle - for (Player player : playerCurrentSong.keySet()) { - Song currentSong = playerCurrentSong.get(player); - if (currentSong != null) { - currentSong.stop(player); - } - } - - Song currentSong = this.songs.get(this.currentlyPlaying); - currentSong.play(trait, trait.getVolume(), trait.getPitch()); - currentlyPlaying++; - schedulerId = Bukkit.getScheduler().scheduleSyncDelayedTask(MinstrelPlugin.getInstance(), () -> play(trait), - currentSong.getDuration() * 20L); + QueueManager.queue(new SongEndTime(player, trait, System.currentTimeMillis() + + (currentSong.getDuration() * 1000L))); } /** @@ -148,4 +114,31 @@ public class Playlist { this.songs.clear(); } + /** + * Changes to the next song in the playlist + */ + public void changeSong() { + if (this.songs.isEmpty()) { + return; + } + + this.currentlyPlaying++; + this.currentlyPlaying = this.currentlyPlaying % this.songs.size(); + Bukkit.getScheduler().scheduleSyncDelayedTask(MinstrelPlugin.getInstance(), this::changeSong, + this.songs.get(this.currentlyPlaying).getDuration() * 20L); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < this.songs.size(); i++) { + builder.append("\n"); + if (this.currentlyPlaying == i) { + builder.append(">"); + } + builder.append(this.songs.get(i)); + } + return builder.toString(); + } + } diff --git a/src/main/java/net/knarcraft/minstrel/music/Song.java b/src/main/java/net/knarcraft/minstrel/music/Song.java index b01484e..e3fd9b7 100644 --- a/src/main/java/net/knarcraft/minstrel/music/Song.java +++ b/src/main/java/net/knarcraft/minstrel/music/Song.java @@ -1,8 +1,6 @@ package net.knarcraft.minstrel.music; -import net.knarcraft.minstrel.MinstrelPlugin; import net.knarcraft.minstrel.trait.MinstrelTrait; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.SoundCategory; import org.bukkit.entity.Player; @@ -21,7 +19,6 @@ public class Song { private final int durationSeconds; private final SoundCategory category; private final String sound; - private boolean isPlaying = false; private final SetVolume of 1 means the max volume, which can be heard up to 1 chunk away. Setting it to 2 means it can be - * heard 2 chunks away.
- * - * @param traitThe minstrel to play this song
- * @param volumeThe volume to play this song at
- * @param pitchThe pitch to play this song at
- */ - public void play(MinstrelTrait trait, float volume, float pitch) { - for (Player player : Bukkit.getOnlinePlayers()) { - play(player, trait.getLocation(), volume, pitch); - } - - this.isPlaying = true; - //Mark this song as ended, once - Bukkit.getScheduler().runTaskLater(MinstrelPlugin.getInstance(), () -> this.isPlaying = false, - durationSeconds * 20L); - } - /** * Gets the duration of this song, in seconds * @@ -90,7 +66,7 @@ public class Song { */ public void stop(Player player) { //Don't bother stopping if not playing - if (!playingFor.contains(player) && !isPlaying) { + if (!playingFor.contains(player)) { return; } playingFor.remove(player); @@ -119,12 +95,7 @@ public class Song { @Override public String toString() { - String songString = this.category + ":" + this.sound + ":" + this.durationSeconds; - if (this.isPlaying) { - return ">" + songString; - } else { - return songString; - } + return this.category + ":" + this.sound + ":" + this.durationSeconds; } /** diff --git a/src/main/java/net/knarcraft/minstrel/music/SongEndTime.java b/src/main/java/net/knarcraft/minstrel/music/SongEndTime.java new file mode 100644 index 0000000..235e0e7 --- /dev/null +++ b/src/main/java/net/knarcraft/minstrel/music/SongEndTime.java @@ -0,0 +1,37 @@ +package net.knarcraft.minstrel.music; + +import net.knarcraft.minstrel.trait.MinstrelTrait; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +/** + * A record of the time when a specific song will end + * + * @param playerThe player the song is playing for
+ * @param minstrelTraitThe minstrel playing the song
+ * @param endTimeThe time when the song will end
+ */ +public record SongEndTime(Player player, MinstrelTrait minstrelTrait, long endTime) implements ComparableThe player to check
+ * @param minstrelTraitThe minstrel to check
+ * @returnTrue if the player would hear the minstrel
+ */ + public static boolean canHear(Player player, MinstrelTrait minstrelTrait) { + double distance = player.getLocation().distance(minstrelTrait.getNPC().getStoredLocation()); + // A player can hear a minstrel 15 blocks away, but the distance increases if volume > 1 + return distance < 15 * (Math.max(minstrelTrait.getVolume(), 1)); + } + +}