diff --git a/setup/src/main/java/net/onelitefeather/cygnus/setup/item/HotBarLayout.java b/setup/src/main/java/net/onelitefeather/cygnus/setup/item/HotBarLayout.java new file mode 100644 index 0000000..0c00e60 --- /dev/null +++ b/setup/src/main/java/net/onelitefeather/cygnus/setup/item/HotBarLayout.java @@ -0,0 +1,72 @@ +package net.onelitefeather.cygnus.setup.item; + +import net.minestom.server.entity.Player; +import net.minestom.server.item.ItemStack; + +import java.util.Arrays; + +/** + * Represents a hotbar layout consisting of nine item slots (indices 0–8). + * Each slot is initially filled with {@link ItemStack#AIR} and can be + * overwritten individually via {@link #set(int, ItemStack)}. + *
+ * Use {@link #apply(Player)} to write the layout into a player's inventory. + * + * @author theEvilReaper + * @version 1.0.0 + * @since 0.1.0 + */ +public final class HotBarLayout { + + private static final int HOTBAR_SIZE = 9; + + private final ItemStack[] items = new ItemStack[HOTBAR_SIZE]; + + /** + * Creates a new layout with all slots set to {@link ItemStack#AIR}. + */ + public HotBarLayout() { + Arrays.fill(items, ItemStack.AIR); + } + + /** + * Places the given item at the specified hotbar slot. + * + * @param slot the slot index (0–8) + * @param item the item to place + * @return this layout, for chaining + * @throws IndexOutOfBoundsException if the slot is not in range 0–8 + */ + public HotBarLayout set(int slot, ItemStack item) { + if (slot < 0 || slot >= HOTBAR_SIZE) { + throw new IndexOutOfBoundsException("Slot must be between 0 and 8, got: " + slot); + } + items[slot] = item; + return this; + } + + /** + * Returns the item at the given hotbar slot. + * + * @param slot the slot index (0–8) + * @return the item at the slot + * @throws IndexOutOfBoundsException if the slot is not in range 0–8 + */ + public ItemStack get(int slot) { + if (slot < 0 || slot >= HOTBAR_SIZE) { + throw new IndexOutOfBoundsException("Slot must be between 0 and 8, got: " + slot); + } + return items[slot]; + } + + /** + * Writes this layout into the player's inventory (slots 0–8). + * + * @param player the player whose hotbar is updated + */ + public void apply(Player player) { + for (int i = 0; i < items.length; i++) { + player.getInventory().setItemStack(i, items[i]); + } + } +} diff --git a/setup/src/main/java/net/onelitefeather/cygnus/setup/util/SetupItems.java b/setup/src/main/java/net/onelitefeather/cygnus/setup/item/SetupItems.java similarity index 53% rename from setup/src/main/java/net/onelitefeather/cygnus/setup/util/SetupItems.java rename to setup/src/main/java/net/onelitefeather/cygnus/setup/item/SetupItems.java index c06367a..63e5a39 100644 --- a/setup/src/main/java/net/onelitefeather/cygnus/setup/util/SetupItems.java +++ b/setup/src/main/java/net/onelitefeather/cygnus/setup/item/SetupItems.java @@ -1,4 +1,4 @@ -package net.onelitefeather.cygnus.setup.util; +package net.onelitefeather.cygnus.setup.item; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -6,7 +6,6 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.onelitefeather.cygnus.common.Tags; -import org.jetbrains.annotations.NotNull; /** * The class holds the {@link ItemStack} references which have some functionality during a setup process from an map. @@ -21,24 +20,36 @@ public final class SetupItems { public static final byte ZERO_INDEX = (byte) 0x00; public static final byte FOURTH_INDEX = (byte) 0x04; - private static final ItemStack mapSelection; + private static final HotBarLayout selectionLayout; + private static final HotBarLayout mapLayout; + private static final HotBarLayout pageLayout; - private static final ItemStack dataItem; - private static final ItemStack saveData; - - static { - mapSelection = ItemStack.builder(Material.CHEST) + static { + selectionLayout = new HotBarLayout(); + selectionLayout.set(FOURTH_INDEX, ItemStack.builder(Material.CHEST) .customName(Component.text("Map selection", NamedTextColor.GREEN)) .set(Tags.ITEM_TAG, ZERO_INDEX) - .build(); - saveData = ItemStack.builder(Material.BELL) + .build() + ); + ItemStack saveItem = ItemStack.builder(Material.BELL) .customName(Component.text("Save data", NamedTextColor.RED)) .set(Tags.ITEM_TAG, (byte) 0x01) .build(); - dataItem = ItemStack.builder(Material.COMPASS) + mapLayout = new HotBarLayout(); + mapLayout.set(2, ItemStack.builder(Material.COMPASS) .customName(Component.text("Data", NamedTextColor.AQUA)) .set(Tags.ITEM_TAG, (byte) 0x02) - .build(); + .build() + ); + mapLayout.set(6, saveItem); + + pageLayout = new HotBarLayout(); + pageLayout.set(2, ItemStack.builder(Material.BARRIER) + .customName(Component.text("Cancel", NamedTextColor.RED)) + .set(Tags.ITEM_TAG, (byte) 0x03) + .build() + ); + pageLayout.set(6, saveItem); } /** @@ -46,8 +57,8 @@ public final class SetupItems { * * @param player the player who should receive the item */ - public static void setMapSelection(@NotNull Player player) { - player.getInventory().setItemStack(FOURTH_INDEX, mapSelection); + public static void setMapSelection(Player player) { + selectionLayout.apply(player); player.setHeldItemSlot(FOURTH_INDEX); } @@ -56,9 +67,18 @@ public static void setMapSelection(@NotNull Player player) { * * @param player the player who should receive the item */ - public static void setSaveData(@NotNull Player player) { - player.getInventory().setItemStack(2, dataItem); - player.getInventory().setItemStack(6, saveData); + public static void setSaveData(Player player) { + mapLayout.apply(player); + player.setHeldItemSlot(ZERO_INDEX); + } + + /** + * Set's the {@link ItemStack} which are required for the page setup. + * + * @param player the player who should receive the item + */ + public static void setPageItems(Player player) { + pageLayout.apply(player); player.setHeldItemSlot(ZERO_INDEX); } diff --git a/setup/src/main/java/net/onelitefeather/cygnus/setup/item/package-info.java b/setup/src/main/java/net/onelitefeather/cygnus/setup/item/package-info.java new file mode 100644 index 0000000..bb43bbd --- /dev/null +++ b/setup/src/main/java/net/onelitefeather/cygnus/setup/item/package-info.java @@ -0,0 +1,4 @@ +@NotNullByDefault +package net.onelitefeather.cygnus.setup.item; + +import org.jetbrains.annotations.NotNullByDefault; \ No newline at end of file diff --git a/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/InstanceAddListener.java b/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/InstanceAddListener.java index 923ad2d..032cd6e 100644 --- a/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/InstanceAddListener.java +++ b/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/InstanceAddListener.java @@ -3,7 +3,7 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.event.instance.AddEntityToInstanceEvent; -import net.onelitefeather.cygnus.setup.util.SetupItems; +import net.onelitefeather.cygnus.setup.item.SetupItems; import java.time.Duration; import java.time.temporal.ChronoUnit; diff --git a/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/PlayerSpawnListener.java b/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/PlayerSpawnListener.java index 0065b81..c65ca01 100644 --- a/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/PlayerSpawnListener.java +++ b/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/PlayerSpawnListener.java @@ -4,7 +4,7 @@ import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerSpawnEvent; -import net.onelitefeather.cygnus.setup.util.SetupItems; +import net.onelitefeather.cygnus.setup.item.SetupItems; import java.util.function.Consumer; diff --git a/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/SetupItemListener.java b/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/SetupItemListener.java index d462a7e..1a74f6e 100644 --- a/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/SetupItemListener.java +++ b/setup/src/main/java/net/onelitefeather/cygnus/setup/listener/SetupItemListener.java @@ -6,7 +6,7 @@ import net.onelitefeather.cygnus.common.Tags; import net.onelitefeather.cygnus.setup.inventory.MapSetupInventory; import net.onelitefeather.cygnus.setup.util.SetupData; -import net.onelitefeather.cygnus.setup.util.SetupItems; +import net.onelitefeather.cygnus.setup.item.SetupItems; import net.onelitefeather.cygnus.setup.util.SetupTags; import java.util.function.Consumer; diff --git a/setup/src/test/java/net/onelitefeather/cygnus/setup/item/HotBarLayoutTest.java b/setup/src/test/java/net/onelitefeather/cygnus/setup/item/HotBarLayoutTest.java new file mode 100644 index 0000000..d9be546 --- /dev/null +++ b/setup/src/test/java/net/onelitefeather/cygnus/setup/item/HotBarLayoutTest.java @@ -0,0 +1,79 @@ +package net.onelitefeather.cygnus.setup.item; + +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class HotBarLayoutTest { + + @Test + void testDefaultSlotsAreAir() { + var layout = new HotBarLayout(); + for (int i = 0; i < 9; i++) { + assertEquals(ItemStack.AIR, layout.get(i), "Slot " + i + " should default to AIR"); + } + } + + @Test + void testSetAndGet() { + var item = ItemStack.of(Material.DIAMOND); + var layout = new HotBarLayout().set(4, item); + assertEquals(item, layout.get(4)); + } + + @Test + void testSetIsChainable() { + var first = ItemStack.of(Material.COMPASS); + var second = ItemStack.of(Material.CLOCK); + var layout = new HotBarLayout() + .set(7, first) + .set(8, second); + + assertEquals(first, layout.get(7)); + assertEquals(second, layout.get(8)); + } + + @Test + void testSetOverwritesPreviousItem() { + var original = ItemStack.of(Material.STONE); + var replacement = ItemStack.of(Material.DIAMOND); + + var layout = new HotBarLayout() + .set(0, original) + .set(0, replacement); + + assertEquals(replacement, layout.get(0)); + } + + @Test + void testUntouchedSlotsRemainAir() { + var layout = new HotBarLayout() + .set(3, ItemStack.of(Material.COMPASS)) + .set(8, ItemStack.of(Material.CLOCK)); + + for (int i = 0; i < 9; i++) { + if (i != 3 && i != 8) { + assertEquals(ItemStack.AIR, layout.get(i), "Slot " + i + " should still be AIR"); + } + } + } + + @ParameterizedTest(name = "Invalid slot index: {0}") + @ValueSource(ints = {-1, 9, 10, -100, Integer.MAX_VALUE}) + void testSetWithInvalidSlotThrows(int slot) { + var layout = new HotBarLayout(); + assertThrows(IndexOutOfBoundsException.class, () -> layout.set(slot, ItemStack.AIR)); + } + + @ParameterizedTest(name = "Invalid get index: {0}") + @ValueSource(ints = {-1, 9, 10, -100, Integer.MAX_VALUE}) + void testGetWithInvalidSlotThrows(int slot) { + var layout = new HotBarLayout(); + assertThrows(IndexOutOfBoundsException.class, () -> layout.get(slot)); + } +} diff --git a/setup/src/test/java/net/onelitefeather/cygnus/setup/util/SetupItemsTest.java b/setup/src/test/java/net/onelitefeather/cygnus/setup/item/SetupItemsTest.java similarity index 83% rename from setup/src/test/java/net/onelitefeather/cygnus/setup/util/SetupItemsTest.java rename to setup/src/test/java/net/onelitefeather/cygnus/setup/item/SetupItemsTest.java index d3e35ca..15d12f9 100644 --- a/setup/src/test/java/net/onelitefeather/cygnus/setup/util/SetupItemsTest.java +++ b/setup/src/test/java/net/onelitefeather/cygnus/setup/item/SetupItemsTest.java @@ -1,4 +1,4 @@ -package net.onelitefeather.cygnus.setup.util; +package net.onelitefeather.cygnus.setup.item; import net.minestom.server.entity.Player; import net.minestom.server.instance.Instance; @@ -42,6 +42,18 @@ void testSaveDataItemSet(@NotNull Env env) { env.destroyInstance(instance, true); } + @Test + void testPageItemSet(@NotNull Env env) { + Instance instance = env.createFlatInstance(); + Player player = env.createPlayer(instance); + + SetupItems.setPageItems(player); + assertItem(player, 2, (byte) 0x03); + assertItem(player, 6, (byte) 0x01); + + env.destroyInstance(instance, true); + } + /** * Asserts that the item in the specified slot of the player's inventory matches the expected item ID. *