Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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)}.
* <p>
* 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]);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
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;
import net.minestom.server.entity.Player;
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.
Expand All @@ -21,33 +20,45 @@ 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);
}

/**
* Set's the {@link ItemStack} which represents the map selection into an inventory.
*
* @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);
}

Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NotNullByDefault
package net.onelitefeather.cygnus.setup.item;

import org.jetbrains.annotations.NotNullByDefault;
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down