feat(items): add bound role control items

This commit is contained in:
dmg
2026-08-21 21:28:32 -04:00
parent ec7d50c2b5
commit 0b202f588d
24 changed files with 777 additions and 13 deletions
@@ -43,6 +43,10 @@ final class PluginSettingsTest {
assertEquals("STICK", settings.assassinItem().material());
assertEquals("Assassin Cloak", settings.assassinItem().name());
assertEquals("FISHING_ROD", settings.tamerItem().material());
assertEquals("NETHER_STAR", settings.tyrantControlItem().material());
assertEquals("Tyrant Control", settings.tyrantControlItem().name());
assertEquals("COMPASS", settings.vigilanteControlItem().material());
assertEquals("Vigilante Control", settings.vigilanteControlItem().name());
assertEquals("/tyrant item", settings.recoveryCommand());
assertEquals("Your inventory is full. Make room and use /tyrant item.",
settings.inventoryFullMessage());
@@ -0,0 +1,89 @@
package games.dmg.spigottyrant;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.Test;
final class RoleControlItemListenerTest {
@Test
void droppedRoleControlItemVanishes() {
ItemStack item = mock(ItemStack.class);
Item dropped = mock(Item.class);
when(dropped.getItemStack()).thenReturn(item);
PlayerDropItemEvent event = mock(PlayerDropItemEvent.class);
when(event.getItemDrop()).thenReturn(dropped);
RoleControlItemService items = mock(RoleControlItemService.class);
when(items.isBoundRoleControlItem(item)).thenReturn(true);
RoleControlItemListener listener = listener(items);
listener.onDrop(event);
verify(dropped).remove();
}
@Test
void automatedInventoryTransferIsBlocked() {
ItemStack item = mock(ItemStack.class);
InventoryMoveItemEvent event = mock(InventoryMoveItemEvent.class);
when(event.getItem()).thenReturn(item);
RoleControlItemService items = mock(RoleControlItemService.class);
when(items.isBoundRoleControlItem(item)).thenReturn(true);
RoleControlItemListener listener = listener(items);
listener.onInventoryMove(event);
verify(event).setCancelled(true);
}
@Test
void currentTyrantUsesBoundItemToOpenControlPanel() {
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
GameState game = new GameState(
GameLifecycle.RUNNING, Optional.of(tyrantId), Optional.empty(),
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
0, 0, Set.of()
);
TyrantStateManager manager = mock(TyrantStateManager.class);
when(manager.game()).thenReturn(game);
Player tyrant = mock(Player.class);
when(tyrant.getUniqueId()).thenReturn(tyrantId);
ItemStack item = mock(ItemStack.class);
PlayerInteractEvent event = mock(PlayerInteractEvent.class);
when(event.getPlayer()).thenReturn(tyrant);
when(event.getItem()).thenReturn(item);
when(event.getAction()).thenReturn(Action.RIGHT_CLICK_AIR);
RoleControlItemService items = mock(RoleControlItemService.class);
when(items.isBoundRoleControlItem(item)).thenReturn(true);
when(items.owner(item)).thenReturn(Optional.of(tyrantId));
when(items.role(item)).thenReturn(Optional.of(RoleControl.TYRANT));
TyrantControlPanel tyrantPanel = mock(TyrantControlPanel.class);
RoleControlItemListener listener = new RoleControlItemListener(
manager, items, tyrantPanel, mock(VigilanteControlPanel.class)
);
listener.onInteract(event);
verify(event).setCancelled(true);
verify(tyrantPanel).open(tyrant);
}
private static RoleControlItemListener listener(RoleControlItemService items) {
return new RoleControlItemListener(
mock(TyrantStateManager.class), items, mock(TyrantControlPanel.class),
mock(VigilanteControlPanel.class)
);
}
}
@@ -0,0 +1,41 @@
package games.dmg.spigottyrant;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.junit.jupiter.api.Test;
final class RoleControlItemPolicyTest {
@Test
void runningRoleHoldersRequireOnlyTheirOwnControlItem() {
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
UUID vigilanteId = UUID.fromString("22222222-2222-2222-2222-222222222222");
GameState game = new GameState(
GameLifecycle.RUNNING, Optional.of(tyrantId), Optional.of(vigilanteId),
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
0, 0, Set.of()
);
assertEquals(Optional.of(RoleControl.TYRANT),
RoleControlItemPolicy.requiredRole(game, tyrantId));
assertEquals(Optional.of(RoleControl.VIGILANTE),
RoleControlItemPolicy.requiredRole(game, vigilanteId));
assertEquals(Optional.empty(), RoleControlItemPolicy.requiredRole(game, UUID.randomUUID()));
}
@Test
void pausedRoleHolderRetainsControlItem() {
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
GameState game = new GameState(
GameLifecycle.PAUSED, Optional.of(tyrantId), Optional.empty(),
Optional.empty(), Optional.empty(), Optional.of(java.time.Instant.EPOCH),
Duration.ZERO, 0, 0, Set.of()
);
assertEquals(Optional.of(RoleControl.TYRANT),
RoleControlItemPolicy.requiredRole(game, tyrantId));
}
}
@@ -0,0 +1,37 @@
package games.dmg.spigottyrant;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.Test;
final class RoleControlItemRefreshTaskTest {
@Test
void reconciliationRemovesInvalidCopiesAndRecoversRequiredRoleItem() {
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
GameState game = new GameState(
GameLifecycle.RUNNING, Optional.of(tyrantId), Optional.empty(),
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
0, 0, Set.of()
);
TyrantStateManager manager = mock(TyrantStateManager.class);
when(manager.game()).thenReturn(game);
Player tyrant = mock(Player.class);
Server server = mock(Server.class);
doReturn(Set.of(tyrant)).when(server).getOnlinePlayers();
RoleControlItemService items = mock(RoleControlItemService.class);
new RoleControlItemRefreshTask(manager, items, server).run();
verify(items).removeInvalid(tyrant, game);
verify(items).recover(tyrant, game);
}
}
@@ -43,6 +43,28 @@ final class TyrantCommandTest {
verify(controlPanel, org.mockito.Mockito.times(2)).open(tyrant);
}
@Test
void itemCommandRecoversTyrantRoleControlItem() {
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
GameState game = new GameState(
GameLifecycle.RUNNING, Optional.of(tyrantId), Optional.empty(),
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
0, 0, Set.of()
);
TyrantStateManager manager = mock(TyrantStateManager.class);
when(manager.game()).thenReturn(game);
Player tyrant = mock(Player.class);
when(tyrant.getUniqueId()).thenReturn(tyrantId);
RoleControlItemService roleItems = mock(RoleControlItemService.class);
TyrantCommand command = new TyrantCommand(
manager, new TyrantProgressionService(), mock(TyrantControlPanel.class), roleItems
);
command.onCommand(tyrant, mock(Command.class), "tyrant", new String[] {"item"});
verify(roleItems).recover(tyrant, game);
}
@Test
void choicesClearlyIdentifyPurchasedAvailableAndUnavailableUnlocks() {
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
@@ -5,12 +5,39 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import org.bukkit.command.Command;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.Test;
final class VigilanteCommandTest {
@Test
void itemCommandRecoversVigilanteRoleControlItem() {
UUID vigilanteId = UUID.fromString("22222222-2222-2222-2222-222222222222");
GameState game = new GameState(
GameLifecycle.RUNNING,
Optional.of(UUID.fromString("11111111-1111-1111-1111-111111111111")),
Optional.of(vigilanteId), Optional.empty(), Optional.empty(), Optional.empty(),
Duration.ZERO, 0, 0, Set.of()
);
TyrantStateManager manager = mock(TyrantStateManager.class);
when(manager.game()).thenReturn(game);
Player vigilante = mock(Player.class);
when(vigilante.getUniqueId()).thenReturn(vigilanteId);
RoleControlItemService roleItems = mock(RoleControlItemService.class);
VigilanteCommand command = new VigilanteCommand(
manager, new FollowerService(), mock(OnlinePlayerDirectory.class),
mock(VigilanteControlPanel.class), roleItems
);
command.onCommand(vigilante, mock(Command.class), "vigilante", new String[] {"item"});
verify(roleItems).recover(vigilante, game);
}
@Test
void playerCanOpenControlPanelWithNoArgumentsOrMenuSubcommand() {
UUID playerId = UUID.fromString("22222222-2222-2222-2222-222222222222");