feat(armor): add Tyrant legacy rewards
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TyrantArmorServiceTest {
|
||||
private static final UUID TYRANT = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID OTHER = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
private final TyrantArmorService service = new TyrantArmorService();
|
||||
|
||||
@Test
|
||||
void fullyUnlockedTyrantSpendsOneChoiceOnAnUnclaimedPiece() {
|
||||
GameState before = game(2, allUnlocks(), Set.of());
|
||||
|
||||
ArmorClaimResult result = service.claim(before, TYRANT, TyrantArmorPiece.HELMET);
|
||||
|
||||
assertEquals(ArmorClaimStatus.CLAIMED, result.status());
|
||||
assertEquals(1, result.state().unspentChoices());
|
||||
assertEquals(Set.of(TyrantArmorPiece.HELMET), result.state().claimedArmor());
|
||||
assertEquals(before.purchases(), result.state().purchases());
|
||||
}
|
||||
|
||||
@Test
|
||||
void eachPieceCanBeClaimedOnlyOncePerReign() {
|
||||
GameState before = game(2, allUnlocks(), Set.of(TyrantArmorPiece.BOOTS));
|
||||
|
||||
ArmorClaimResult result = service.claim(before, TYRANT, TyrantArmorPiece.BOOTS);
|
||||
|
||||
assertEquals(ArmorClaimStatus.ALREADY_CLAIMED, result.status());
|
||||
assertEquals(before, result.state());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsIncompleteUnlocksNoChoicesNonTyrantsAndPausedGames() {
|
||||
GameState complete = game(0, allUnlocks(), Set.of());
|
||||
GameState incomplete = game(1, Set.of(TyrantUnlock.ASSASSIN), Set.of());
|
||||
GameState paused = new GameState(
|
||||
GameLifecycle.PAUSED, complete.tyrantId(), complete.vigilanteId(),
|
||||
complete.pendingTyrant(), complete.pendingVigilante(),
|
||||
Optional.of(java.time.Instant.parse("2026-09-04T00:00:00Z")), Duration.ZERO,
|
||||
1, 1, allUnlocks(), Set.of()
|
||||
);
|
||||
|
||||
assertEquals(ArmorClaimStatus.NO_CHOICES,
|
||||
service.claim(complete, TYRANT, TyrantArmorPiece.HELMET).status());
|
||||
assertEquals(ArmorClaimStatus.UNLOCKS_INCOMPLETE,
|
||||
service.claim(incomplete, TYRANT, TyrantArmorPiece.HELMET).status());
|
||||
assertEquals(ArmorClaimStatus.NOT_TYRANT,
|
||||
service.claim(game(1, allUnlocks(), Set.of()), OTHER,
|
||||
TyrantArmorPiece.HELMET).status());
|
||||
assertEquals(ArmorClaimStatus.GAME_NOT_RUNNING,
|
||||
service.claim(paused, TYRANT, TyrantArmorPiece.HELMET).status());
|
||||
}
|
||||
|
||||
private static Set<TyrantUnlock> allUnlocks() {
|
||||
return EnumSet.allOf(TyrantUnlock.class);
|
||||
}
|
||||
|
||||
private static GameState game(
|
||||
int choices,
|
||||
Set<TyrantUnlock> unlocks,
|
||||
Set<TyrantArmorPiece> claimed
|
||||
) {
|
||||
return new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.empty(),
|
||||
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
|
||||
6, choices, unlocks, claimed
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TyrantArmorSpecTest {
|
||||
@Test
|
||||
void everyPieceIsNetheriteWithNamedLoreAndApprovedEnchantments() {
|
||||
assertEquals(Material.NETHERITE_HELMET,
|
||||
TyrantArmorSpec.forPiece(TyrantArmorPiece.HELMET, "Alex").material());
|
||||
assertEquals(Material.NETHERITE_CHESTPLATE,
|
||||
TyrantArmorSpec.forPiece(TyrantArmorPiece.CHESTPLATE, "Alex").material());
|
||||
assertEquals(Material.NETHERITE_LEGGINGS,
|
||||
TyrantArmorSpec.forPiece(TyrantArmorPiece.LEGGINGS, "Alex").material());
|
||||
assertEquals(Material.NETHERITE_BOOTS,
|
||||
TyrantArmorSpec.forPiece(TyrantArmorPiece.BOOTS, "Alex").material());
|
||||
|
||||
TyrantArmorSpec spec = TyrantArmorSpec.forPiece(TyrantArmorPiece.HELMET, "Alex");
|
||||
assertEquals("Tyrant's Alex Helmet", spec.displayName());
|
||||
assertTrue(spec.lore().stream().anyMatch(line -> line.contains("Alex")));
|
||||
assertEquals(5, spec.protectionLevel());
|
||||
assertEquals(1, spec.mendingLevel());
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,16 @@ import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
@@ -97,6 +101,74 @@ final class TyrantCommandTest {
|
||||
&& message.contains("ASSASSIN")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullyUnlockedTyrantCanClaimNamedArmorIntoInventory() {
|
||||
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,
|
||||
6, 2, EnumSet.allOf(TyrantUnlock.class), Set.of()
|
||||
);
|
||||
TyrantStateManager manager = mock(TyrantStateManager.class);
|
||||
when(manager.snapshot()).thenReturn(new PersistentState(
|
||||
game, Map.of(tyrantId, PlayerState.newPlayer(tyrantId, "Alex"))
|
||||
));
|
||||
PlayerInventory inventory = mock(PlayerInventory.class);
|
||||
when(inventory.firstEmpty()).thenReturn(4);
|
||||
Player tyrant = mock(Player.class);
|
||||
when(tyrant.getUniqueId()).thenReturn(tyrantId);
|
||||
when(tyrant.getName()).thenReturn("Alex");
|
||||
when(tyrant.getInventory()).thenReturn(inventory);
|
||||
ItemStack reward = mock(ItemStack.class);
|
||||
TyrantArmorItemFactory factory = mock(TyrantArmorItemFactory.class);
|
||||
when(factory.create(TyrantArmorPiece.HELMET, "Alex")).thenReturn(reward);
|
||||
TyrantCommand command = new TyrantCommand(
|
||||
manager, new TyrantProgressionService(), factory
|
||||
);
|
||||
|
||||
command.onCommand(
|
||||
tyrant, mock(Command.class), "tyrant", new String[] {"armor", "helmet"}
|
||||
);
|
||||
|
||||
verify(manager).replaceState(org.mockito.ArgumentMatchers.argThat(state ->
|
||||
state.game().unspentChoices() == 1
|
||||
&& state.game().claimedArmor().equals(Set.of(TyrantArmorPiece.HELMET))
|
||||
));
|
||||
verify(inventory).setItem(4, reward);
|
||||
verify(manager).saveIfDirty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullInventoryDoesNotConsumeChoiceOrCreateArmor() {
|
||||
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,
|
||||
6, 1, EnumSet.allOf(TyrantUnlock.class), Set.of()
|
||||
);
|
||||
TyrantStateManager manager = mock(TyrantStateManager.class);
|
||||
when(manager.snapshot()).thenReturn(new PersistentState(
|
||||
game, Map.of(tyrantId, PlayerState.newPlayer(tyrantId, "Alex"))
|
||||
));
|
||||
PlayerInventory inventory = mock(PlayerInventory.class);
|
||||
when(inventory.firstEmpty()).thenReturn(-1);
|
||||
Player tyrant = mock(Player.class);
|
||||
when(tyrant.getUniqueId()).thenReturn(tyrantId);
|
||||
when(tyrant.getInventory()).thenReturn(inventory);
|
||||
TyrantArmorItemFactory factory = mock(TyrantArmorItemFactory.class);
|
||||
TyrantCommand command = new TyrantCommand(
|
||||
manager, new TyrantProgressionService(), factory
|
||||
);
|
||||
|
||||
command.onCommand(
|
||||
tyrant, mock(Command.class), "tyrant", new String[] {"armor", "boots"}
|
||||
);
|
||||
|
||||
verify(manager, org.mockito.Mockito.never()).replaceState(any());
|
||||
verify(factory, org.mockito.Mockito.never()).create(any(), any());
|
||||
verify(tyrant).sendMessage(org.mockito.ArgumentMatchers.contains("inventory is full"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void assigningClassNotifiesTyrantAndNewHolder() {
|
||||
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -46,4 +49,24 @@ final class TyrantControlPanelModelTest {
|
||||
assertEquals("Blade", model.classHolders().get(TyrantClass.ASSASSIN));
|
||||
assertEquals(Duration.ofSeconds(90), model.intelligenceCooldown());
|
||||
}
|
||||
|
||||
@Test
|
||||
void armorAvailabilityRequiresAllUnlocksAChoiceAndAnUnclaimedSlot() {
|
||||
UUID tyrantId = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
Instant now = Instant.parse("2026-09-04T00:00:00Z");
|
||||
GameState game = new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(tyrantId), Optional.empty(),
|
||||
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
|
||||
6, 1, EnumSet.allOf(TyrantUnlock.class), Set.of(TyrantArmorPiece.HELMET)
|
||||
);
|
||||
PlayerState tyrant = PlayerState.newPlayer(tyrantId, "Tyrant");
|
||||
|
||||
TyrantControlPanelModel model = TyrantControlPanelModel.create(
|
||||
game, tyrant, Map.of(tyrantId, tyrant), now
|
||||
);
|
||||
|
||||
assertTrue(model.allStandardUnlocksPurchased());
|
||||
assertFalse(model.armorAvailable(TyrantArmorPiece.HELMET));
|
||||
assertTrue(model.armorAvailable(TyrantArmorPiece.CHESTPLATE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,13 @@ final class TyrantProgressionServiceTest {
|
||||
|
||||
@Test
|
||||
void administrativePointGrantAddsOneLevelAndChoicePerInvocation() {
|
||||
GameState before = runningState(2, 1, Set.of(TyrantUnlock.ASSASSIN));
|
||||
GameState base = runningState(2, 1, Set.of(TyrantUnlock.ASSASSIN));
|
||||
GameState before = new GameState(
|
||||
base.lifecycle(), base.tyrantId(), base.vigilanteId(), base.pendingTyrant(),
|
||||
base.pendingVigilante(), base.pausedAt(), base.accumulatedPausedTime(),
|
||||
base.tyrantLevel(), base.unspentChoices(), base.purchases(),
|
||||
Set.of(TyrantArmorPiece.HELMET)
|
||||
);
|
||||
|
||||
GameState first = service.grantPoint(before);
|
||||
GameState second = service.grantPoint(first);
|
||||
@@ -60,6 +66,7 @@ final class TyrantProgressionServiceTest {
|
||||
assertEquals(before.purchases(), second.purchases());
|
||||
assertEquals(before.tyrantId(), second.tyrantId());
|
||||
assertEquals(before.vigilanteId(), second.vigilanteId());
|
||||
assertEquals(before.claimedArmor(), second.claimedArmor());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -123,17 +130,24 @@ final class TyrantProgressionServiceTest {
|
||||
|
||||
@Test
|
||||
void endingReignClearsLevelChoicesAndPurchases() {
|
||||
GameState progressed = runningState(
|
||||
GameState base = runningState(
|
||||
4,
|
||||
2,
|
||||
Set.of(TyrantUnlock.ASSASSIN, TyrantUnlock.STRENGTH)
|
||||
);
|
||||
GameState progressed = new GameState(
|
||||
base.lifecycle(), base.tyrantId(), base.vigilanteId(), base.pendingTyrant(),
|
||||
base.pendingVigilante(), base.pausedAt(), base.accumulatedPausedTime(),
|
||||
base.tyrantLevel(), base.unspentChoices(), base.purchases(),
|
||||
Set.of(TyrantArmorPiece.BOOTS)
|
||||
);
|
||||
|
||||
GameState reset = service.resetReignProgression(progressed);
|
||||
|
||||
assertEquals(0, reset.tyrantLevel());
|
||||
assertEquals(0, reset.unspentChoices());
|
||||
assertEquals(Set.of(), reset.purchases());
|
||||
assertEquals(Set.of(), reset.claimedArmor());
|
||||
}
|
||||
|
||||
private static GameState runningState(
|
||||
|
||||
@@ -30,6 +30,7 @@ final class TyrantSuccessionServiceTest {
|
||||
assertEquals(0, after.game().tyrantLevel());
|
||||
assertEquals(1, after.game().unspentChoices());
|
||||
assertEquals(Set.of(), after.game().purchases());
|
||||
assertEquals(Set.of(), after.game().claimedArmor());
|
||||
assertEquals(Optional.empty(), after.game().vigilanteId());
|
||||
assertEquals(Optional.empty(), after.game().pendingVigilante());
|
||||
assertEquals(
|
||||
@@ -57,7 +58,8 @@ final class TyrantSuccessionServiceTest {
|
||||
GameState game = new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(OLD_TYRANT), Optional.of(OLD_VIGILANTE),
|
||||
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
|
||||
5, 2, Set.of(TyrantUnlock.ASSASSIN, TyrantUnlock.STRENGTH)
|
||||
5, 2, Set.of(TyrantUnlock.ASSASSIN, TyrantUnlock.STRENGTH),
|
||||
Set.of(TyrantArmorPiece.CHESTPLATE)
|
||||
);
|
||||
Map<UUID, PlayerState> players = Map.of(
|
||||
OLD_TYRANT, active(new PlayerState(
|
||||
|
||||
@@ -25,6 +25,12 @@ final class TyrantTabCompleterTest {
|
||||
List<String> buy = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {"buy", "ro"}
|
||||
);
|
||||
List<String> armor = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {"armor", "ch"}
|
||||
);
|
||||
List<String> armorExtra = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {"armor", "helmet", ""}
|
||||
);
|
||||
List<String> assign = completer.onTabComplete(
|
||||
player, command, "tyrant", new String[] {"assign", "f"}
|
||||
);
|
||||
@@ -32,9 +38,13 @@ final class TyrantTabCompleterTest {
|
||||
player, command, "tyrant", new String[] {"relinquish", "c"}
|
||||
);
|
||||
|
||||
assertTrue(root.containsAll(List.of("menu", "status", "buy", "assign", "item")));
|
||||
assertTrue(root.containsAll(List.of(
|
||||
"menu", "status", "buy", "armor", "assign", "item"
|
||||
)));
|
||||
assertFalse(root.contains("SomePlayer"));
|
||||
assertEquals(List.of("roster_intelligence"), buy);
|
||||
assertEquals(List.of("chestplate"), armor);
|
||||
assertEquals(List.of(), armorExtra);
|
||||
assertEquals(List.of("fixer"), assign);
|
||||
assertEquals(List.of("confirm"), confirm);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ final class YamlTyrantStateRepositoryTest {
|
||||
Duration.ofMinutes(15),
|
||||
3,
|
||||
2,
|
||||
Set.of(TyrantUnlock.ASSASSIN, TyrantUnlock.STRENGTH)
|
||||
Set.of(TyrantUnlock.ASSASSIN, TyrantUnlock.STRENGTH),
|
||||
Set.of(TyrantArmorPiece.HELMET, TyrantArmorPiece.BOOTS)
|
||||
);
|
||||
PlayerState player = new PlayerState(
|
||||
tyrant,
|
||||
|
||||
Reference in New Issue
Block a user