diff --git a/gradle.properties b/gradle.properties index c69cbd6b..d32a071a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ loader_version_range=[4,) mod_id=sophisticatedcore mod_name=Sophisticated Core mod_license=GNU General Public License v3.0 -mod_version=1.0.13 +mod_version=1.1.0 mod_group_id=sophisticatedcore mod_authors=P3pp3rF1y mod_description=A library / shared functionality mod for Sophisticated Storage and Backpacks diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/SettingsTabBase.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/SettingsTabBase.java index 67a89373..3a371990 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/SettingsTabBase.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/SettingsTabBase.java @@ -20,7 +20,7 @@ public abstract class SettingsTabBase> extends Tab { private static final int RIGHT_BORDER_WIDTH = 6; - private static final int BOTTOM_BORDER_HEIGHT = 7; + private static final int BOTTOM_BORDER_HEIGHT = 6; protected final T screen; protected Dimension openTabDimension = new Dimension(0, 0); diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/Tab.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/Tab.java index 2e28ea31..b28cada3 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/Tab.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/client/gui/Tab.java @@ -88,8 +88,10 @@ public Optional getTabRectangle() { @Override protected void renderBg(GuiGraphics guiGraphics, Minecraft minecraft, int mouseX, int mouseY) { int halfHeight = height / 2; + int oddHeightAddition = height % 2; + int secondHalfHeight = halfHeight + oddHeightAddition; guiGraphics.blit(GuiHelper.GUI_CONTROLS, x, y, (float) TEXTURE_WIDTH - width, 0, width, halfHeight, TEXTURE_WIDTH, TEXTURE_HEIGHT); - guiGraphics.blit(GuiHelper.GUI_CONTROLS, x, y + halfHeight, (float) TEXTURE_WIDTH - width, (float) TEXTURE_HEIGHT - halfHeight, width, halfHeight, TEXTURE_WIDTH, TEXTURE_HEIGHT); + guiGraphics.blit(GuiHelper.GUI_CONTROLS, x, y + halfHeight, (float) TEXTURE_WIDTH - width, (float) TEXTURE_HEIGHT - secondHalfHeight, width, secondHalfHeight, TEXTURE_WIDTH, TEXTURE_HEIGHT); guiGraphics.blit(GuiHelper.GUI_CONTROLS, x - 3, y, TEXTURE_WIDTH / 2, TEXTURE_HEIGHT - height, 3, height); } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModCoreDataComponents.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModCoreDataComponents.java index 9e69ebc0..b94c5b4a 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModCoreDataComponents.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModCoreDataComponents.java @@ -17,6 +17,7 @@ import net.p3pp3rf1y.sophisticatedcore.upgrades.FilterAttributes; import net.p3pp3rf1y.sophisticatedcore.upgrades.feeding.HungerLevel; import net.p3pp3rf1y.sophisticatedcore.upgrades.filter.Direction; +import net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox.RepeatMode; import net.p3pp3rf1y.sophisticatedcore.upgrades.xppump.AutomationDirection; import net.p3pp3rf1y.sophisticatedcore.util.SimpleItemContent; @@ -152,6 +153,18 @@ public class ModCoreDataComponents { public static final Supplier> ENABLED = DATA_COMPONENT_TYPES.register("enabled", () -> new DataComponentType.Builder().persistent(Codec.BOOL).networkSynchronized(ByteBufCodecs.BOOL).build()); + public static final Supplier> REPEAT_MODE = DATA_COMPONENT_TYPES.register("repeat_mode", + () -> new DataComponentType.Builder().persistent(RepeatMode.CODEC).networkSynchronized(RepeatMode.STREAM_CODEC).build()); + + public static final Supplier> SHUFFLE = DATA_COMPONENT_TYPES.register("shuffle", + () -> new DataComponentType.Builder().persistent(Codec.BOOL).networkSynchronized(ByteBufCodecs.BOOL).build()); + + public static final Supplier> DISC_SLOT_ACTIVE = DATA_COMPONENT_TYPES.register("disc_slot_active", + () -> new DataComponentType.Builder().persistent(Codec.INT).networkSynchronized(ByteBufCodecs.INT).build()); + + public static final Supplier> DISC_FINISH_TIME = DATA_COMPONENT_TYPES.register("disc_finish_time", + () -> new DataComponentType.Builder().persistent(Codec.LONG).networkSynchronized(ByteBufCodecs.VAR_LONG).build()); + public static void register(IEventBus modBus) { DATA_COMPONENT_TYPES.register(modBus); } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModPayloads.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModPayloads.java index af203896..031240e5 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModPayloads.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/init/ModPayloads.java @@ -5,7 +5,7 @@ import net.p3pp3rf1y.sophisticatedcore.SophisticatedCore; import net.p3pp3rf1y.sophisticatedcore.network.*; import net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox.PlayDiscPayload; -import net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox.SoundStopNotificationPayload; +import net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox.SoundFinishedNotificationPayload; import net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox.StopDiscPlaybackPayload; import net.p3pp3rf1y.sophisticatedcore.upgrades.tank.TankClickPayload; @@ -21,7 +21,7 @@ public static void registerPayloads(final RegisterPayloadHandlersEvent event) { registrar.playToClient(SyncPlayerSettingsPayload.TYPE, SyncPlayerSettingsPayload.STREAM_CODEC, SyncPlayerSettingsPayload::handlePayload); registrar.playToClient(PlayDiscPayload.TYPE, PlayDiscPayload.STREAM_CODEC, PlayDiscPayload::handlePayload); registrar.playToClient(StopDiscPlaybackPayload.TYPE, StopDiscPlaybackPayload.STREAM_CODEC, StopDiscPlaybackPayload::handlePayload); - registrar.playToServer(SoundStopNotificationPayload.TYPE, SoundStopNotificationPayload.STREAM_CODEC, SoundStopNotificationPayload::handlePayload); + registrar.playToServer(SoundFinishedNotificationPayload.TYPE, SoundFinishedNotificationPayload.STREAM_CODEC, SoundFinishedNotificationPayload::handlePayload); registrar.playToServer(TankClickPayload.TYPE, TankClickPayload.STREAM_CODEC, TankClickPayload::handlePayload); registrar.playToServer(TransferItemsPayload.TYPE, TransferItemsPayload.STREAM_CODEC, TransferItemsPayload::handlePayload); registrar.playToClient(SyncTemplateSettingsPayload.TYPE, SyncTemplateSettingsPayload.STREAM_CODEC, SyncTemplateSettingsPayload::handlePayload); diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeConfig.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeConfig.java new file mode 100644 index 00000000..4388b7a9 --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeConfig.java @@ -0,0 +1,16 @@ +package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; + +import net.neoforged.neoforge.common.ModConfigSpec; + +public class JukeboxUpgradeConfig { + public final ModConfigSpec.IntValue numberOfSlots; + public final ModConfigSpec.IntValue slotsInRow; + + public JukeboxUpgradeConfig(ModConfigSpec.Builder builder, String upgradeName, String path, int defaultNumberOfSlots) { + builder.comment(upgradeName + " Settings").push(path); + numberOfSlots = builder.comment("Number of slots for discs in jukebox upgrade").defineInRange("numberOfSlots", defaultNumberOfSlots, 1, 16); + slotsInRow = builder.comment("Number of lots displayed in a row").defineInRange("slotsInRow", 4, 1, 6); + + builder.pop(); + } +} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeContainer.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeContainer.java index 2e47afc7..bc01de9e 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeContainer.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeContainer.java @@ -1,42 +1,60 @@ package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; +import net.minecraft.core.Holder; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.JukeboxSong; +import net.minecraft.world.level.Level; import net.neoforged.neoforge.items.SlotItemHandler; import net.p3pp3rf1y.sophisticatedcore.common.gui.StorageContainerMenuBase; import net.p3pp3rf1y.sophisticatedcore.common.gui.UpgradeContainerBase; import net.p3pp3rf1y.sophisticatedcore.common.gui.UpgradeContainerType; import net.p3pp3rf1y.sophisticatedcore.util.NBTHelper; -public class JukeboxUpgradeContainer extends UpgradeContainerBase { +import java.util.Optional; + +public class JukeboxUpgradeContainer extends UpgradeContainerBase { private static final String ACTION_DATA = "action"; - public JukeboxUpgradeContainer(Player player, int upgradeContainerId, JukeboxUpgradeItem.Wrapper upgradeWrapper, UpgradeContainerType type) { + public JukeboxUpgradeContainer(Player player, int upgradeContainerId, JukeboxUpgradeWrapper upgradeWrapper, UpgradeContainerType type) { super(player, upgradeContainerId, upgradeWrapper, type); - slots.add(new SlotItemHandler(upgradeWrapper.getDiscInventory(), 0, -100, -100) { - @Override - public void setChanged() { - super.setChanged(); - if (upgradeWrapper.isPlaying()) { - upgradeWrapper.stop(player); + for (int slot = 0; slot < upgradeWrapper.getDiscInventory().getSlots(); slot++) { + slots.add(new SlotItemHandler(upgradeWrapper.getDiscInventory(), slot, -100, -100) { + @Override + public void setChanged() { + super.setChanged(); + if (upgradeWrapper.isPlaying() && getSlotIndex() == upgradeWrapper.getDiscSlotActive()) { + upgradeWrapper.stop(player); + } } - } - }); + }); + } } @Override public void handlePacket(CompoundTag data) { if (data.contains(ACTION_DATA)) { String actionName = data.getString(ACTION_DATA); - if (actionName.equals("play")) { - if (player.containerMenu instanceof StorageContainerMenuBase storageContainerMenu) { - storageContainerMenu.getBlockPosition().ifPresentOrElse(pos -> upgradeWrapper.play(player.level(), pos), () -> upgradeWrapper.play(storageContainerMenu.getEntity().orElse(player))); + switch (actionName) { + case "play" -> { + if (player.containerMenu instanceof StorageContainerMenuBase storageContainerMenu) { + storageContainerMenu.getBlockPosition().ifPresentOrElse(pos -> upgradeWrapper.play(player.level(), pos), () -> upgradeWrapper.play(storageContainerMenu.getEntity().orElse(player))); + } } - } else if (actionName.equals("stop")) { - upgradeWrapper.stop(player); + case "stop" -> upgradeWrapper.stop(player); + case "next" -> upgradeWrapper.next(); + case "previous" -> upgradeWrapper.previous(); } } + if (data.contains("shuffle")) { + upgradeWrapper.setShuffleEnabled(data.getBoolean("shuffle")); + } + + if (data.contains("repeat")) { + NBTHelper.getEnumConstant(data, "repeat", RepeatMode::fromName).ifPresent(upgradeWrapper::setRepeatMode); + } } public void play() { @@ -46,4 +64,45 @@ public void play() { public void stop() { sendDataToServer(() -> NBTHelper.putString(new CompoundTag(), ACTION_DATA, "stop")); } + + public void next() { + sendDataToServer(() -> NBTHelper.putString(new CompoundTag(), ACTION_DATA, "next")); + } + + public void previous() { + sendDataToServer(() -> NBTHelper.putString(new CompoundTag(), ACTION_DATA, "previous")); + } + + public boolean isShuffleEnabled() { + return upgradeWrapper.isShuffleEnabled(); + } + + public void toggleShuffle() { + boolean newValue = !upgradeWrapper.isShuffleEnabled(); + upgradeWrapper.setShuffleEnabled(newValue); + sendBooleanToServer("shuffle", newValue); + } + + public RepeatMode getRepeatMode() { + return upgradeWrapper.getRepeatMode(); + } + + public void toggleRepeat() { + RepeatMode newValue = upgradeWrapper.getRepeatMode().next(); + upgradeWrapper.setRepeatMode(newValue); + sendDataToServer(() -> NBTHelper.putEnumConstant(new CompoundTag(), "repeat", newValue)); + } + + public Optional getDiscSlotActive() { + int discSlotActive = upgradeWrapper.getDiscSlotActive(); + return discSlotActive > -1 ? Optional.of(slots.get(discSlotActive)) : Optional.empty(); + } + + public long getDiscFinishTime() { + return upgradeWrapper.getDiscFinishTime(); + } + + public Optional> getJukeboxSong(Level level) { + return upgradeWrapper.getJukeboxSongHolder(level); + } } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeItem.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeItem.java index 05c7cd6c..5d25be52 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeItem.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeItem.java @@ -1,35 +1,28 @@ package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; -import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponents; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.JukeboxSong; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.Vec3; -import net.neoforged.neoforge.items.ComponentItemHandler; -import net.neoforged.neoforge.items.IItemHandler; -import net.p3pp3rf1y.sophisticatedcore.api.IStorageWrapper; -import net.p3pp3rf1y.sophisticatedcore.init.ModCoreDataComponents; -import net.p3pp3rf1y.sophisticatedcore.upgrades.*; +import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.TranslationHelper; +import net.p3pp3rf1y.sophisticatedcore.upgrades.IUpgradeCountLimitConfig; +import net.p3pp3rf1y.sophisticatedcore.upgrades.UpgradeGroup; +import net.p3pp3rf1y.sophisticatedcore.upgrades.UpgradeItemBase; +import net.p3pp3rf1y.sophisticatedcore.upgrades.UpgradeType; -import javax.annotation.Nullable; import java.util.List; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Consumer; +import java.util.function.IntSupplier; -public class JukeboxUpgradeItem extends UpgradeItemBase { - public static final UpgradeType TYPE = new UpgradeType<>(Wrapper::new); +public class JukeboxUpgradeItem extends UpgradeItemBase { + public static final UpgradeGroup UPGRADE_GROUP = new UpgradeGroup("jukebox_upgrades", TranslationHelper.INSTANCE.translUpgradeGroup("jukebox_upgrades")); + public static final UpgradeType TYPE = new UpgradeType<>(JukeboxUpgradeWrapper::new); + private final IntSupplier numberOfSlots; + private final IntSupplier slotsInRow; - public JukeboxUpgradeItem(IUpgradeCountLimitConfig upgradeTypeLimitConfig) { + public JukeboxUpgradeItem(IUpgradeCountLimitConfig upgradeTypeLimitConfig, IntSupplier numberOfSlots, IntSupplier slotsInRow) { super(upgradeTypeLimitConfig); + this.numberOfSlots = numberOfSlots; + this.slotsInRow = slotsInRow; } @Override - public UpgradeType getType() { + public UpgradeType getType() { return TYPE; } @@ -38,101 +31,17 @@ public List getUpgradeConflicts() { return List.of(); } - public static class Wrapper extends UpgradeWrapperBase implements ITickableUpgrade { - private static final int KEEP_ALIVE_SEND_INTERVAL = 5; - private final ComponentItemHandler discInventory; - private long lastKeepAliveSendTime = 0; - private boolean isPlaying; - - protected Wrapper(IStorageWrapper storageWrapper, ItemStack upgrade, Consumer upgradeSaveHandler) { - super(storageWrapper, upgrade, upgradeSaveHandler); - discInventory = new ComponentItemHandler(upgrade, DataComponents.CONTAINER, 1) { - @Override - protected void onContentsChanged(int slot, ItemStack oldStack, ItemStack newStack) { - super.onContentsChanged(slot, oldStack, newStack); - save(); - } - - @Override - public boolean isItemValid(int slot, ItemStack stack) { - return stack.isEmpty() || stack.has(DataComponents.JUKEBOX_PLAYABLE); - } - }; - isPlaying = upgrade.getOrDefault(ModCoreDataComponents.IS_PLAYING, false); - } - - public void setDisc(ItemStack disc) { - discInventory.setStackInSlot(0, disc); - } - - public ItemStack getDisc() { - return discInventory.getStackInSlot(0); - } - - public void play(Level level, BlockPos pos) { - play(level, (serverLevel, storageUuid) -> JukeboxSong.fromStack(level.registryAccess(), getDisc()) - .ifPresent(song -> ServerStorageSoundHandler.startPlayingDisc(serverLevel, pos, storageUuid, song, () -> setIsPlaying(false)))); - } - - public void play(Entity entity) { - play(entity.level(), (world, storageUuid) -> JukeboxSong.fromStack(entity.level().registryAccess(), getDisc()) - .ifPresent(song -> ServerStorageSoundHandler.startPlayingDisc(world, entity.position(), storageUuid, entity.getId(), song, () -> setIsPlaying(false)))); - } - - private void play(Level level, BiConsumer play) { - if (!(level instanceof ServerLevel) || getDisc().isEmpty()) { - return; - } - storageWrapper.getContentsUuid().ifPresent(storageUuid -> play.accept((ServerLevel) level, storageUuid)); - setIsPlaying(true); - } - - private void setIsPlaying(boolean playing) { - isPlaying = playing; - upgrade.set(ModCoreDataComponents.IS_PLAYING, playing); - if (isPlaying) { - storageWrapper.getRenderInfo().setUpgradeRenderData(JukeboxUpgradeRenderData.TYPE, new JukeboxUpgradeRenderData(true)); - } else { - removeRenderData(); - } - save(); - } - - private void removeRenderData() { - storageWrapper.getRenderInfo().removeUpgradeRenderData(JukeboxUpgradeRenderData.TYPE); - } - - public void stop(LivingEntity entity) { - if (!(entity.level() instanceof ServerLevel)) { - return; - } - storageWrapper.getContentsUuid().ifPresent(storageUuid -> - ServerStorageSoundHandler.stopPlayingDisc(entity.level(), entity.position(), storageUuid) - ); - setIsPlaying(false); - } - - public IItemHandler getDiscInventory() { - return discInventory; - } - - @Override - public void tick(@Nullable Entity entity, Level level, BlockPos pos) { - if (isPlaying && lastKeepAliveSendTime < level.getGameTime() - KEEP_ALIVE_SEND_INTERVAL) { - storageWrapper.getContentsUuid().ifPresent(storageUuid -> - ServerStorageSoundHandler.updateKeepAlive(storageUuid, level, entity != null ? entity.position() : Vec3.atCenterOf(pos), () -> setIsPlaying(false)) - ); - lastKeepAliveSendTime = level.getGameTime(); - } - } + @Override + public UpgradeGroup getUpgradeGroup() { + return UPGRADE_GROUP; + } - public boolean isPlaying() { - return isPlaying; - } + public int getNumberOfSlots() { + return numberOfSlots.getAsInt(); + } - @Override - public void onBeforeRemoved() { - removeRenderData(); - } + public int getSlotsInRow() { + return slotsInRow.getAsInt(); } + } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeTab.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeTab.java index bed19e8b..f24e7a6f 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeTab.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeTab.java @@ -1,57 +1,170 @@ package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; +import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.core.Holder; import net.minecraft.network.chat.Component; import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.JukeboxSong; import net.p3pp3rf1y.sophisticatedcore.client.gui.StorageScreenBase; import net.p3pp3rf1y.sophisticatedcore.client.gui.UpgradeSettingsTab; import net.p3pp3rf1y.sophisticatedcore.client.gui.controls.Button; import net.p3pp3rf1y.sophisticatedcore.client.gui.controls.ButtonDefinition; -import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.Dimension; -import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.GuiHelper; -import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.Position; -import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.TextureBlitData; -import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.TranslationHelper; -import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.UV; +import net.p3pp3rf1y.sophisticatedcore.client.gui.controls.ToggleButton; +import net.p3pp3rf1y.sophisticatedcore.client.gui.utils.*; + +import java.util.Map; +import java.util.Optional; import static net.p3pp3rf1y.sophisticatedcore.client.gui.utils.GuiHelper.*; -public class JukeboxUpgradeTab extends UpgradeSettingsTab { +public abstract class JukeboxUpgradeTab extends UpgradeSettingsTab { private static final TextureBlitData PLAY_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(16, 64), Dimension.SQUARE_16); - private static final ButtonDefinition PLAY = new ButtonDefinition(Dimension.SQUARE_16, DEFAULT_BUTTON_BACKGROUND, DEFAULT_BUTTON_HOVERED_BACKGROUND, PLAY_FOREGROUND, + private static final ButtonDefinition PLAY = new ButtonDefinition(Dimension.SQUARE_18, DEFAULT_BUTTON_BACKGROUND, DEFAULT_BUTTON_HOVERED_BACKGROUND, PLAY_FOREGROUND, Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("play"))); private static final TextureBlitData STOP_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(0, 64), Dimension.SQUARE_16); - private static final ButtonDefinition STOP = new ButtonDefinition(Dimension.SQUARE_16, DEFAULT_BUTTON_BACKGROUND, DEFAULT_BUTTON_HOVERED_BACKGROUND, STOP_FOREGROUND, + private static final ButtonDefinition STOP = new ButtonDefinition(Dimension.SQUARE_18, DEFAULT_BUTTON_BACKGROUND, DEFAULT_BUTTON_HOVERED_BACKGROUND, STOP_FOREGROUND, Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("stop"))); - public JukeboxUpgradeTab(JukeboxUpgradeContainer upgradeContainer, Position position, StorageScreenBase screen) { - super(upgradeContainer, position, screen, TranslationHelper.INSTANCE.translUpgrade("jukebox"), TranslationHelper.INSTANCE.translUpgradeTooltip("jukebox")); + private static final TextureBlitData SHUFFLE_ON_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(96, 80), Dimension.SQUARE_16); + private static final TextureBlitData SHUFFLE_OFF_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(112, 80), Dimension.SQUARE_16); + private static final ButtonDefinition.Toggle SHUFFLE = new ButtonDefinition.Toggle<>(Dimension.SQUARE_18, DEFAULT_BUTTON_BACKGROUND, + Map.of( + true, new ToggleButton.StateData(SHUFFLE_ON_FOREGROUND, Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("shuffle_on"))), + false, new ToggleButton.StateData(SHUFFLE_OFF_FOREGROUND, Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("shuffle_off"))) + ), DEFAULT_BUTTON_HOVERED_BACKGROUND); - addHideableChild(new Button(new Position(x + 3, y + 44), STOP, button -> { - if (button == 0) { - getContainer().stop(); - } - })); - addHideableChild(new Button(new Position(x + 21, y + 44), PLAY, button -> { - if (button == 0) { - getContainer().play(); - } - })); + private static final TextureBlitData REPEAT_ALL_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(128, 80), Dimension.SQUARE_16); + private static final TextureBlitData REPEAT_ONE_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(144, 80), Dimension.SQUARE_16); + private static final TextureBlitData NO_REPEAT_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(160, 80), Dimension.SQUARE_16); + private static final ButtonDefinition.Toggle REPEAT = new ButtonDefinition.Toggle<>(Dimension.SQUARE_18, DEFAULT_BUTTON_BACKGROUND, + Map.of( + RepeatMode.ALL, new ToggleButton.StateData(REPEAT_ALL_FOREGROUND, Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("repeat_all"))), + RepeatMode.ONE, new ToggleButton.StateData(REPEAT_ONE_FOREGROUND, Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("repeat_one"))), + RepeatMode.NO, new ToggleButton.StateData(NO_REPEAT_FOREGROUND, Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("no_repeat"))) + ), DEFAULT_BUTTON_HOVERED_BACKGROUND); + + private static final TextureBlitData PREVIOUS_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(48, 96), Dimension.SQUARE_16); + private static final ButtonDefinition PREVIOUS = new ButtonDefinition(Dimension.SQUARE_18, DEFAULT_BUTTON_BACKGROUND, DEFAULT_BUTTON_HOVERED_BACKGROUND, PREVIOUS_FOREGROUND, + Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("previous_disc"))); + + private static final TextureBlitData NEXT_FOREGROUND = new TextureBlitData(ICONS, new Position(1, 1), Dimension.SQUARE_256, new UV(32, 96), Dimension.SQUARE_16); + private static final ButtonDefinition NEXT = new ButtonDefinition(Dimension.SQUARE_18, DEFAULT_BUTTON_BACKGROUND, DEFAULT_BUTTON_HOVERED_BACKGROUND, NEXT_FOREGROUND, + Component.translatable(TranslationHelper.INSTANCE.translUpgradeButton("next_disc"))); + private static final int BUTTON_PADDING = 3; + public static final int TOP_Y = 24; + + private final int slotsInRow; + + public JukeboxUpgradeTab(JukeboxUpgradeContainer upgradeContainer, Position position, StorageScreenBase screen, int slotsInRow, Component tabLabel, Component closedTooltip) { + super(upgradeContainer, position, screen, tabLabel, closedTooltip); + this.slotsInRow = slotsInRow; } @Override protected void renderBg(GuiGraphics guiGraphics, Minecraft minecraft, int mouseX, int mouseY) { super.renderBg(guiGraphics, minecraft, mouseX, mouseY); if (getContainer().isOpen()) { - GuiHelper.renderSlotsBackground(guiGraphics, x + 3, y + 24, 1, 1); + GuiHelper.renderSlotsBackground(guiGraphics, x + 3, y + 24, slotsInRow, getContainer().getSlots().size() / slotsInRow, getContainer().getSlots().size() % slotsInRow); } } @Override protected void moveSlotsToTab() { - Slot discSlot = getContainer().getSlots().get(0); - discSlot.x = x - screen.getGuiLeft() + 4; - discSlot.y = y - screen.getGuiTop() + 25; + int slotIndex = 0; + for (Slot discSlot : getContainer().getSlots()) { + discSlot.x = x - screen.getGuiLeft() + 4 + (slotIndex % slotsInRow) * 18; + discSlot.y = y - screen.getGuiTop() + TOP_Y + 1 + (slotIndex / slotsInRow) * 18; + slotIndex++; + } + } + + protected int getBottomSlotY() { + return TOP_Y + (getContainer().getSlots().size() / slotsInRow) * 18 + (getContainer().getSlots().size() % slotsInRow > 0 ? 18 : 0); + } + + public static class Basic extends JukeboxUpgradeTab { + public Basic(JukeboxUpgradeContainer upgradeContainer, Position position, StorageScreenBase screen) { + super(upgradeContainer, position, screen, 4, TranslationHelper.INSTANCE.translUpgrade("jukebox"), TranslationHelper.INSTANCE.translUpgradeTooltip("jukebox")); + int bottomSlotY = getBottomSlotY(); + addHideableChild(new Button(new Position(x + 3, y + bottomSlotY + BUTTON_PADDING), STOP, button -> { + if (button == 0) { + getContainer().stop(); + } + })); + addHideableChild(new Button(new Position(x + 21, y + bottomSlotY + BUTTON_PADDING), PLAY, button -> { + if (button == 0) { + getContainer().play(); + } + })); + } + } + + public static class Advanced extends JukeboxUpgradeTab { + public Advanced(JukeboxUpgradeContainer upgradeContainer, Position position, StorageScreenBase screen, int slotsInRow) { + super(upgradeContainer, position, screen, slotsInRow, TranslationHelper.INSTANCE.translUpgrade("advanced_jukebox"), TranslationHelper.INSTANCE.translUpgradeTooltip("advanced_jukebox")); + int bottomSlotY = getBottomSlotY(); + addHideableChild(new Button(new Position(x + 3, y + bottomSlotY + BUTTON_PADDING), PREVIOUS, button -> { + if (button == 0) { + getContainer().previous(); + } + })); + addHideableChild(new Button(new Position(x + 21, y + bottomSlotY + BUTTON_PADDING), STOP, button -> { + if (button == 0) { + getContainer().stop(); + } + })); + addHideableChild(new Button(new Position(x + 39, y + bottomSlotY + BUTTON_PADDING), PLAY, button -> { + if (button == 0) { + getContainer().play(); + } + })); + addHideableChild(new Button(new Position(x + 57, y + bottomSlotY + BUTTON_PADDING), NEXT, button -> { + if (button == 0) { + getContainer().next(); + } + })); + addHideableChild(new ToggleButton<>(new Position(x + 12, y + bottomSlotY + BUTTON_PADDING + 20), SHUFFLE, button -> { + if (button == 0) { + getContainer().toggleShuffle(); + } + }, () -> getContainer().isShuffleEnabled())); + + addHideableChild(new ToggleButton<>(new Position(x + 48, y + bottomSlotY + BUTTON_PADDING + 20), REPEAT, button -> { + if (button == 0) { + getContainer().toggleRepeat(); + } + }, () -> getContainer().getRepeatMode())); + } + + @Override + protected void renderBg(GuiGraphics guiGraphics, Minecraft minecraft, int mouseX, int mouseY) { + super.renderBg(guiGraphics, minecraft, mouseX, mouseY); + getContainer().getDiscSlotActive().ifPresent(slot -> renderPlaytimeOverLay(guiGraphics, 0x55_00CC00, screen.getLeftX() + slot.x, screen.getTopY() + slot.y, 16, 16)); + } + + private float getPlaybackRemainingProgress() { + long finishTime = getContainer().getDiscFinishTime(); + int remaining = (int) (finishTime - minecraft.level.getGameTime()); + + Optional> song = getContainer().getJukeboxSong(minecraft.level); + + return song.map(jukeboxSongHolder -> (remaining / (float) jukeboxSongHolder.value().lengthInTicks())).orElse(0f); + } + + private void renderPlaytimeOverLay(GuiGraphics guiGraphics, int slotColor, int xPos, int yPos, int width, int height) { + float remainingProgress = getPlaybackRemainingProgress(); + if (remainingProgress <= 0) { + return; + } + int progressOver = width - (int) (width * remainingProgress); + + RenderSystem.disableDepthTest(); + RenderSystem.colorMask(true, true, true, false); + guiGraphics.fillGradient(xPos + progressOver, yPos, xPos + width, yPos + height, 0, slotColor, slotColor); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.enableDepthTest(); + } } } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeWrapper.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeWrapper.java new file mode 100644 index 00000000..ee49f06a --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/JukeboxUpgradeWrapper.java @@ -0,0 +1,287 @@ +package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.component.DataComponents; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.JukeboxSong; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.items.ComponentItemHandler; +import net.neoforged.neoforge.items.IItemHandler; +import net.p3pp3rf1y.sophisticatedcore.api.IStorageWrapper; +import net.p3pp3rf1y.sophisticatedcore.init.ModCoreDataComponents; +import net.p3pp3rf1y.sophisticatedcore.upgrades.ITickableUpgrade; +import net.p3pp3rf1y.sophisticatedcore.upgrades.UpgradeWrapperBase; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Consumer; + +public class JukeboxUpgradeWrapper extends UpgradeWrapperBase implements ITickableUpgrade { + private static final int KEEP_ALIVE_SEND_INTERVAL = 5; + private final ComponentItemHandler discInventory; + private long lastKeepAliveSendTime = 0; + private boolean isPlaying; + + private final LinkedList playlist = new LinkedList<>(); + private final LinkedList history = new LinkedList<>(); + + private final Set discsRemoved = new HashSet<>(); + private final Set discsAdded = new HashSet<>(); + + @Nullable + private Entity entityPlaying = null; + @Nullable + private Level levelPlaying = null; + @Nullable + private BlockPos posPlaying = null; + + private final Runnable onFinishedCallback = this::onDiscFinished; + + protected JukeboxUpgradeWrapper(IStorageWrapper storageWrapper, ItemStack upgrade, Consumer upgradeSaveHandler) { + super(storageWrapper, upgrade, upgradeSaveHandler); + discInventory = new ComponentItemHandler(upgrade, DataComponents.CONTAINER, upgradeItem.getNumberOfSlots()) { + @Override + protected void onContentsChanged(int slot, ItemStack oldStack, ItemStack newStack) { + super.onContentsChanged(slot, oldStack, newStack); + save(); + if (oldStack.isEmpty() && !newStack.isEmpty()) { + discsAdded.add(slot); + discsRemoved.remove(slot); + } else if (!oldStack.isEmpty() && newStack.isEmpty()) { + discsRemoved.add(slot); + discsAdded.remove(slot); + } + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + return stack.isEmpty() || stack.has(DataComponents.JUKEBOX_PLAYABLE); + } + }; + isPlaying = upgrade.getOrDefault(ModCoreDataComponents.IS_PLAYING, false); + } + + public boolean isShuffleEnabled() { + return upgrade.getOrDefault(ModCoreDataComponents.SHUFFLE, false); + } + + public void setShuffleEnabled(boolean shuffleEnabled) { + upgrade.set(ModCoreDataComponents.SHUFFLE, shuffleEnabled); + save(); + + initPlaylist(true); + } + + public RepeatMode getRepeatMode() { + return upgrade.getOrDefault(ModCoreDataComponents.REPEAT_MODE, RepeatMode.NO); + } + + public void setRepeatMode(RepeatMode repeatMode) { + upgrade.set(ModCoreDataComponents.REPEAT_MODE, repeatMode); + save(); + } + + public ItemStack getDisc() { + return getDiscSlotActive() > -1 ? discInventory.getStackInSlot(getDiscSlotActive()) : ItemStack.EMPTY; + } + + public int getDiscSlotActive() { + return upgrade.getOrDefault(ModCoreDataComponents.DISC_SLOT_ACTIVE, -1); + } + + private void setDiscSlotActive(int discSlotActive) { + upgrade.set(ModCoreDataComponents.DISC_SLOT_ACTIVE, discSlotActive); + save(); + } + + public void play(Level level, BlockPos pos) { + if (isPlaying) { + return; + } + + levelPlaying = level; + posPlaying = pos; + playNext(); + } + + public void play(Entity entity) { + if (isPlaying) { + return; + } + entityPlaying = entity; + playNext(); + } + + private void playDisc() { + Level level = entityPlaying != null ? entityPlaying.level() : levelPlaying; + if (!(level instanceof ServerLevel serverLevel) || (posPlaying == null && entityPlaying == null)) { + return; + } + if (getDisc().isEmpty()) { + return; + } + + storageWrapper.getContentsUuid().ifPresent(storageUuid -> getJukeboxSongHolder(level).ifPresent(song -> { + if (entityPlaying != null) { + ServerStorageSoundHandler.startPlayingDisc(serverLevel, entityPlaying.position(), storageUuid, entityPlaying.getId(), song, onFinishedCallback); + } else { + ServerStorageSoundHandler.startPlayingDisc(serverLevel, posPlaying, storageUuid, song, onFinishedCallback); + } + upgrade.set(ModCoreDataComponents.DISC_FINISH_TIME, level.getGameTime() + song.value().lengthInTicks()); + })); + setIsPlaying(true); + } + + public Optional> getJukeboxSongHolder(Level level) { + return JukeboxSong.fromStack(level.registryAccess(), getDisc()); + } + + private void onDiscFinished() { + if (getRepeatMode() == RepeatMode.ONE) { + playDisc(); + } else if (getRepeatMode() == RepeatMode.ALL) { + playNext(); + } else { + playNext(false); + } + } + + private void setIsPlaying(boolean playing) { + isPlaying = playing; + upgrade.set(ModCoreDataComponents.IS_PLAYING, playing); + if (isPlaying) { + storageWrapper.getRenderInfo().setUpgradeRenderData(JukeboxUpgradeRenderData.TYPE, new JukeboxUpgradeRenderData(true)); + } else { + removeRenderData(); + setDiscSlotActive(-1); + } + save(); + } + + private void removeRenderData() { + storageWrapper.getRenderInfo().removeUpgradeRenderData(JukeboxUpgradeRenderData.TYPE); + } + + public void stop(LivingEntity entity) { + if (!(entity.level() instanceof ServerLevel)) { + return; + } + storageWrapper.getContentsUuid().ifPresent(storageUuid -> + ServerStorageSoundHandler.stopPlayingDisc(entity.level(), entity.position(), storageUuid) + ); + setIsPlaying(false); + upgrade.remove(ModCoreDataComponents.DISC_FINISH_TIME); + setDiscSlotActive(-1); + } + + public IItemHandler getDiscInventory() { + return discInventory; + } + + @Override + public void tick(@Nullable Entity entity, Level level, BlockPos pos) { + if (!level.isClientSide()) { + if (!discsRemoved.isEmpty()) { + discsRemoved.forEach(index -> { + playlist.remove(index); + history.remove(index); + }); + discsRemoved.clear(); + } + if (!discsAdded.isEmpty()) { + playlist.addAll(discsAdded); + discsAdded.clear(); + } + } + + if (isPlaying && lastKeepAliveSendTime < level.getGameTime() - KEEP_ALIVE_SEND_INTERVAL) { + storageWrapper.getContentsUuid().ifPresent(storageUuid -> + ServerStorageSoundHandler.updateKeepAlive(storageUuid, level, entity != null ? entity.position() : Vec3.atCenterOf(pos), () -> setIsPlaying(false)) + ); + lastKeepAliveSendTime = level.getGameTime(); + } + } + + public boolean isPlaying() { + return isPlaying; + } + + @Override + public void onBeforeRemoved() { + removeRenderData(); + } + + public void next() { + if (!isPlaying) { + return; + } + playNext(); + } + + public void playNext() { + playNext(true); + } + + public void playNext(boolean startOverIfAtTheEnd) { + if (playlist.isEmpty() && startOverIfAtTheEnd) { + initPlaylist(false); + } + if (playlist.isEmpty()) { + return; + } + if (getDiscSlotActive() != -1) { + history.add(getDiscSlotActive()); + if (history.size() > discInventory.getSlots()) { + history.poll(); + } + } + Integer discIndex = playlist.poll(); + if (discIndex == null) { + return; + } + setDiscSlotActive(discIndex); + + playDisc(); + } + + private void initPlaylist(boolean excludeActive) { + playlist.clear(); + for (int i = 0; i < discInventory.getSlots(); i++) { + if (!discInventory.getStackInSlot(i).isEmpty() && (!excludeActive || !isPlaying || i != getDiscSlotActive())) { + playlist.add(i); + } + } + if (isShuffleEnabled()) { + Collections.shuffle(playlist); + } + } + + public void previous() { + if (!isPlaying) { + return; + } + playPrevious(); + } + + public void playPrevious() { + if (history.isEmpty()) { + return; + } + playlist.addFirst(getDiscSlotActive()); + Integer discIndex = history.pollLast(); + if (discIndex == null) { + return; + } + setDiscSlotActive(discIndex); + playDisc(); + } + + public long getDiscFinishTime() { + return upgrade.getOrDefault(ModCoreDataComponents.DISC_FINISH_TIME, 0L); + } +} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/RepeatMode.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/RepeatMode.java new file mode 100644 index 00000000..a3e4d462 --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/RepeatMode.java @@ -0,0 +1,50 @@ +package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; + +import com.google.common.collect.ImmutableMap; +import com.mojang.serialization.Codec; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.util.StringRepresentable; +import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs; + +import java.util.Map; + +public enum RepeatMode implements StringRepresentable { + ALL("all"), + ONE("one"), + NO("no"); + + public static final Codec CODEC = StringRepresentable.fromEnum(RepeatMode::values); + public static final StreamCodec STREAM_CODEC = NeoForgeStreamCodecs.enumCodec(RepeatMode.class); + + private final String name; + + RepeatMode(String name) { + this.name = name; + } + + @Override + public String getSerializedName() { + return name; + } + + public RepeatMode next() { + return VALUES[(ordinal() + 1) % VALUES.length]; + } + + private static final Map NAME_VALUES; + private static final RepeatMode[] VALUES; + + static { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + for (RepeatMode value : RepeatMode.values()) { + builder.put(value.getSerializedName(), value); + } + NAME_VALUES = builder.build(); + VALUES = values(); + } + + public static RepeatMode fromName(String name) { + return NAME_VALUES.getOrDefault(name, NO); + } +} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/ServerStorageSoundHandler.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/ServerStorageSoundHandler.java index 6dad2d39..a958cb1d 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/ServerStorageSoundHandler.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/ServerStorageSoundHandler.java @@ -54,17 +54,17 @@ public static void updateKeepAlive(UUID storageUuid, Level level, Vec3 position, } } - public static void onSoundStopped(Level level, UUID storageUuid) { - removeKeepAliveInfo(level, storageUuid); + public static void onSoundFinished(Level level, UUID storageUuid) { + removeKeepAliveInfo(level, storageUuid, true); } private static class KeepAliveInfo { - private final WeakReference onStopHandler; + private final WeakReference onFinishedHandler; private long lastKeepAliveTime; private Vec3 lastPosition; - private KeepAliveInfo(Runnable onStopHandler, long lastKeepAliveTime, Vec3 lastPosition) { - this.onStopHandler = new WeakReference<>(onStopHandler); + private KeepAliveInfo(Runnable onFinishedHandler, long lastKeepAliveTime, Vec3 lastPosition) { + this.onFinishedHandler = new WeakReference<>(onFinishedHandler); this.lastKeepAliveTime = lastKeepAliveTime; this.lastPosition = lastPosition; } @@ -82,18 +82,18 @@ public void update(long gameTime, Vec3 position) { lastPosition = position; } - public void runOnStop() { - Runnable handler = onStopHandler.get(); + public void runOnFinished() { + Runnable handler = onFinishedHandler.get(); if (handler != null) { handler.run(); } } } - public static void startPlayingDisc(ServerLevel serverLevel, BlockPos position, UUID storageUuid, Holder song, Runnable onStopHandler) { + public static void startPlayingDisc(ServerLevel serverLevel, BlockPos position, UUID storageUuid, Holder song, Runnable onFinishedHandler) { Vec3 pos = Vec3.atCenterOf(position); PacketDistributor.sendToPlayersNear(serverLevel, null, pos.x, pos.y, pos.z, 128, new PlayDiscPayload(storageUuid, song, position)); - putKeepAliveInfo(serverLevel, storageUuid, onStopHandler, pos); + putKeepAliveInfo(serverLevel, storageUuid, onFinishedHandler, pos); } public static void startPlayingDisc(ServerLevel serverLevel, Vec3 position, UUID storageUuid, int entityId, Holder song, Runnable onStopHandler) { @@ -101,19 +101,22 @@ public static void startPlayingDisc(ServerLevel serverLevel, Vec3 position, UUID putKeepAliveInfo(serverLevel, storageUuid, onStopHandler, position); } - private static void putKeepAliveInfo(ServerLevel serverLevel, UUID storageUuid, Runnable onStopHandler, Vec3 pos) { - worldStorageSoundKeepAlive.computeIfAbsent(serverLevel.dimension(), dim -> new HashMap<>()).put(storageUuid, new KeepAliveInfo(onStopHandler, serverLevel.getGameTime(), pos)); + private static void putKeepAliveInfo(ServerLevel serverLevel, UUID storageUuid, Runnable onFinishedHandler, Vec3 pos) { + worldStorageSoundKeepAlive.computeIfAbsent(serverLevel.dimension(), dim -> new HashMap<>()).put(storageUuid, new KeepAliveInfo(onFinishedHandler, serverLevel.getGameTime(), pos)); } public static void stopPlayingDisc(Level level, Vec3 position, UUID storageUuid) { - removeKeepAliveInfo(level, storageUuid); + removeKeepAliveInfo(level, storageUuid, false); sendStopMessage(level, position, storageUuid); } - private static void removeKeepAliveInfo(Level level, UUID storageUuid) { + private static void removeKeepAliveInfo(Level level, UUID storageUuid, boolean finished) { ResourceKey dim = level.dimension(); if (worldStorageSoundKeepAlive.containsKey(dim) && worldStorageSoundKeepAlive.get(dim).containsKey(storageUuid)) { - worldStorageSoundKeepAlive.get(dim).remove(storageUuid).runOnStop(); + KeepAliveInfo keepAliveInfo = worldStorageSoundKeepAlive.get(dim).remove(storageUuid); + if (finished) { + keepAliveInfo.runOnFinished(); + } } } diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/SoundFinishedNotificationPayload.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/SoundFinishedNotificationPayload.java new file mode 100644 index 00000000..c093be13 --- /dev/null +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/SoundFinishedNotificationPayload.java @@ -0,0 +1,27 @@ +package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; + +import io.netty.buffer.ByteBuf; +import net.minecraft.core.UUIDUtil; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.p3pp3rf1y.sophisticatedcore.SophisticatedCore; + +import java.util.UUID; + +public record SoundFinishedNotificationPayload(UUID storageUuid) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(SophisticatedCore.getRL("sound_finished_notification")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + UUIDUtil.STREAM_CODEC, + SoundFinishedNotificationPayload::storageUuid, + SoundFinishedNotificationPayload::new); + + @Override + public Type type() { + return TYPE; + } + + public static void handlePayload(SoundFinishedNotificationPayload payload, IPayloadContext context) { + ServerStorageSoundHandler.onSoundFinished(context.player().level(), payload.storageUuid); + } +} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/SoundStopNotificationPayload.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/SoundStopNotificationPayload.java deleted file mode 100644 index 3b8c3018..00000000 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/SoundStopNotificationPayload.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.p3pp3rf1y.sophisticatedcore.upgrades.jukebox; - -import io.netty.buffer.ByteBuf; -import net.minecraft.core.UUIDUtil; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import net.p3pp3rf1y.sophisticatedcore.SophisticatedCore; - -import java.util.UUID; - -public record SoundStopNotificationPayload(UUID storageUuid) implements CustomPacketPayload { - public static final Type TYPE = new Type<>(SophisticatedCore.getRL("sound_stop_notification")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - UUIDUtil.STREAM_CODEC, - SoundStopNotificationPayload::storageUuid, - SoundStopNotificationPayload::new); - - @Override - public Type type() { - return TYPE; - } - - public static void handlePayload(SoundStopNotificationPayload payload, IPayloadContext context) { - ServerStorageSoundHandler.onSoundStopped(context.player().level(), payload.storageUuid); - } -} diff --git a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/StorageSoundHandler.java b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/StorageSoundHandler.java index c4e14d86..b120ae63 100644 --- a/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/StorageSoundHandler.java +++ b/src/main/java/net/p3pp3rf1y/sophisticatedcore/upgrades/jukebox/StorageSoundHandler.java @@ -35,7 +35,6 @@ public static void playStorageSound(UUID storageUuid, SoundInstance sound) { public static void stopStorageSound(UUID storageUuid) { if (storageSounds.containsKey(storageUuid)) { Minecraft.getInstance().getSoundManager().stop(storageSounds.remove(storageUuid)); - PacketDistributor.sendToServer(new SoundStopNotificationPayload(storageUuid)); } } @@ -44,7 +43,7 @@ public static void tick(LevelTickEvent.Post event) { lastPlaybackChecked = event.getLevel().getGameTime(); storageSounds.entrySet().removeIf(entry -> { if (!Minecraft.getInstance().getSoundManager().isActive(entry.getValue())) { - PacketDistributor.sendToServer(new SoundStopNotificationPayload(entry.getKey())); + PacketDistributor.sendToServer(new SoundFinishedNotificationPayload(entry.getKey())); return true; } return false; diff --git a/src/main/resources/assets/sophisticatedcore/lang/en_us.json b/src/main/resources/assets/sophisticatedcore/lang/en_us.json index c7652649..57f28f1e 100644 --- a/src/main/resources/assets/sophisticatedcore/lang/en_us.json +++ b/src/main/resources/assets/sophisticatedcore/lang/en_us.json @@ -13,6 +13,7 @@ "item.sophisticatedcore.storage.tooltip.shift": "Left Shift", "upgrade_group.sophisticatedcore.stack_upgrades": "Stack Upgrades", "upgrade_group.sophisticatedcore.cooking_upgrades": "Furnace Upgrades", + "upgrade_group.sophisticatedcore.jukebox_upgrades": "Jukebox Upgrades", "gui.sophisticatedcore.settings.no_sort": "No Sort", "gui.sophisticatedcore.settings.no_sort.tooltip": "No Sort Slot Settings", "gui.sophisticatedcore.settings.no_sort.tooltip_detail": "Allows selecting slots that are ignored by sorting\nOpen tab to modify slot settings", @@ -46,6 +47,7 @@ "gui.sophisticatedcore.upgrades.crafting": "Craft", "gui.sophisticatedcore.upgrades.stonecutter": "Stonecutter", "gui.sophisticatedcore.upgrades.jukebox": "Jukebox", + "gui.sophisticatedcore.upgrades.advanced_jukebox": "Jukebox", "gui.sophisticatedcore.upgrades.tank": "Tank", "gui.sophisticatedcore.upgrades.pump": "Pump", "gui.sophisticatedcore.upgrades.advanced_pump": "Adv. Pump", @@ -86,6 +88,7 @@ "gui.sophisticatedcore.upgrades.chipped_alchemy_bench.tooltip": "Alchemy Bench", "gui.sophisticatedcore.upgrades.chipped_tinkering_table.tooltip": "Tinkering Table", "gui.sophisticatedcore.upgrades.jukebox.tooltip": "Jukebox", + "gui.sophisticatedcore.upgrades.advanced_jukebox.tooltip": "Advanced Jukebox", "gui.sophisticatedcore.upgrades.tank.tooltip": "Tank", "gui.sophisticatedcore.upgrades.pump.tooltip": "Pump", "gui.sophisticatedcore.upgrades.advanced_pump.tooltip": "Advanced Pump", @@ -158,6 +161,13 @@ "gui.sophisticatedcore.upgrades.buttons.previous_result": "Previous", "gui.sophisticatedcore.upgrades.buttons.next_result": "Next", "gui.sophisticatedcore.upgrades.buttons.select_result": "Select Result", + "gui.sophisticatedcore.upgrades.buttons.shuffle_on": "Shuffle Enabled", + "gui.sophisticatedcore.upgrades.buttons.shuffle_off": "Shuffle Disabled", + "gui.sophisticatedcore.upgrades.buttons.repeat_all": "Repeat All", + "gui.sophisticatedcore.upgrades.buttons.repeat_one": "Repeat One", + "gui.sophisticatedcore.upgrades.buttons.no_repeat": "Repeat Disabled", + "gui.sophisticatedcore.upgrades.buttons.previous_disc": "Previous", + "gui.sophisticatedcore.upgrades.buttons.next_disc": "Next", "gui.sophisticatedcore.upgrades.controls.xp_level_select": "%s lvls", "gui.sophisticatedcore.upgrades.controls.xp_level_select.tooltip": "Level at which Pump Stops", "gui.sophisticatedcore.upgrades.controls.xp_level_select.tooltip.controls": "Scroll to change", diff --git a/src/main/resources/assets/sophisticatedcore/textures/gui/icons.png b/src/main/resources/assets/sophisticatedcore/textures/gui/icons.png index 5eec75cc..ea0a199a 100644 Binary files a/src/main/resources/assets/sophisticatedcore/textures/gui/icons.png and b/src/main/resources/assets/sophisticatedcore/textures/gui/icons.png differ