feat(tyrant): add arena boss succession
This commit is contained in:
@@ -20,6 +20,7 @@ final class DefaultConfigurationTest {
|
||||
PluginSettings settings = PluginSettings.from(values);
|
||||
assertEquals(50.0, settings.tyrantRangeBlocks());
|
||||
assertEquals(Duration.ofDays(7), settings.optOutDuration());
|
||||
assertEquals(Duration.ofMinutes(1), settings.roleMaintenanceInterval());
|
||||
assertEquals("FISHING_ROD", settings.tamerItem().material());
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ final class PluginSettingsTest {
|
||||
assertEquals(50.0, settings.followerRangeBlocks());
|
||||
assertEquals(Duration.ofDays(7), settings.optOutDuration());
|
||||
assertEquals(Duration.ofHours(48), settings.roleInactivity());
|
||||
assertEquals(Duration.ofHours(24), settings.candidateActivityWindow());
|
||||
assertEquals(Duration.ofHours(24), settings.pendingSelectionTimeout());
|
||||
assertEquals(Duration.ofMinutes(1), settings.roleMaintenanceInterval());
|
||||
assertEquals(Duration.ofHours(1), settings.assassinCooldown());
|
||||
assertEquals(Duration.ofMinutes(10), settings.assassinInvisibilityDuration());
|
||||
assertEquals(Duration.ofHours(1), settings.fixerCooldown());
|
||||
@@ -30,12 +29,18 @@ final class PluginSettingsTest {
|
||||
assertEquals(10.0, settings.vigilanteBossArmor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsLegacySelectionRetryAsMaintenanceInterval() {
|
||||
PluginSettings settings = PluginSettings.from(Map.of("selection-retry-seconds", 30));
|
||||
|
||||
assertEquals(Duration.ofSeconds(30), settings.roleMaintenanceInterval());
|
||||
}
|
||||
|
||||
@Test
|
||||
void approvedDefaultsCoverAbilitiesItemsMobsAndMessages() {
|
||||
PluginSettings settings = PluginSettings.from(Map.of());
|
||||
|
||||
assertEquals(Duration.ofMinutes(1), settings.selectionRetryInterval());
|
||||
assertEquals(Duration.ofSeconds(60), settings.assassinDoubleJumpCooldown());
|
||||
assertEquals(Duration.ofSeconds(60), settings.assassinDoubleJumpCooldown());
|
||||
assertEquals(Duration.ofSeconds(15), settings.assassinSpeedDuration());
|
||||
assertEquals(Duration.ofSeconds(20), settings.assassinWeaknessDuration());
|
||||
assertEquals(3, settings.assassinWeaknessLevel());
|
||||
@@ -71,6 +76,10 @@ final class PluginSettingsTest {
|
||||
IllegalArgumentException.class,
|
||||
() -> PluginSettings.from(Map.of("role-inactivity-seconds", -1))
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> PluginSettings.from(Map.of("role-maintenance-seconds", 0))
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> PluginSettings.from(Map.of("vigilante-arena-radius-blocks", 0))
|
||||
|
||||
+40
-3
@@ -1,5 +1,7 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.contains;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
@@ -12,15 +14,23 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class VigilanteArenaControllerTest {
|
||||
final class RoleArenaControllerTest {
|
||||
private static final UUID TYRANT = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID PLAYER = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
|
||||
@Test
|
||||
void boundaryColorsIdentifyTheOpenRole() {
|
||||
assertEquals(Color.RED, RoleArenaController.boundaryColor(ArenaRole.TYRANT));
|
||||
assertEquals(Color.BLUE, RoleArenaController.boundaryColor(ArenaRole.VIGILANTE));
|
||||
assertNull(RoleArenaController.boundaryColor(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void optedInPlayerJoiningDuringVacancyReceivesArenaCoordinates() {
|
||||
Player player = mock(Player.class);
|
||||
@@ -32,7 +42,7 @@ final class VigilanteArenaControllerTest {
|
||||
when(locations.location()).thenReturn(Optional.of(
|
||||
new ArenaLocation("world", 10.0, 64.0, -5.0, 0.0F, 0.0F)
|
||||
));
|
||||
VigilanteArenaController controller = new VigilanteArenaController(
|
||||
RoleArenaController controller = new RoleArenaController(
|
||||
manager, mock(Server.class), locations, PluginSettings.from(Map.of())
|
||||
);
|
||||
|
||||
@@ -41,6 +51,26 @@ final class VigilanteArenaControllerTest {
|
||||
verify(player).sendMessage(contains("world at 10, 64, -5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void optedInPlayerIsNotifiedOfTyrantVacancy() {
|
||||
Player player = mock(Player.class);
|
||||
when(player.getUniqueId()).thenReturn(PLAYER);
|
||||
TyrantStateManager manager = mock(TyrantStateManager.class);
|
||||
when(manager.game()).thenReturn(tyrantVacancy());
|
||||
when(manager.players()).thenReturn(Map.of(PLAYER, PlayerState.newPlayer(PLAYER, "Player")));
|
||||
ArenaLocationStore locations = mock(ArenaLocationStore.class);
|
||||
when(locations.location()).thenReturn(Optional.of(
|
||||
new ArenaLocation("world", 10.0, 64.0, -5.0, 0.0F, 0.0F)
|
||||
));
|
||||
RoleArenaController controller = new RoleArenaController(
|
||||
manager, mock(Server.class), locations, PluginSettings.from(Map.of())
|
||||
);
|
||||
|
||||
controller.onJoin(mockJoin(player));
|
||||
|
||||
verify(player).sendMessage(contains("Tyrant position is open"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void optedOutPlayerDoesNotReceiveVacancyNotice() {
|
||||
Player player = mock(Player.class);
|
||||
@@ -56,7 +86,7 @@ final class VigilanteArenaControllerTest {
|
||||
when(locations.location()).thenReturn(Optional.of(
|
||||
new ArenaLocation("world", 10.0, 64.0, -5.0, 0.0F, 0.0F)
|
||||
));
|
||||
VigilanteArenaController controller = new VigilanteArenaController(
|
||||
RoleArenaController controller = new RoleArenaController(
|
||||
manager, mock(Server.class), locations, PluginSettings.from(Map.of())
|
||||
);
|
||||
|
||||
@@ -71,6 +101,13 @@ final class VigilanteArenaControllerTest {
|
||||
return event;
|
||||
}
|
||||
|
||||
private static GameState tyrantVacancy() {
|
||||
return new GameState(
|
||||
GameLifecycle.RUNNING, Optional.empty(), Optional.empty(), Optional.empty(),
|
||||
Optional.empty(), Optional.empty(), Duration.ZERO, 0, 1, Set.of()
|
||||
);
|
||||
}
|
||||
|
||||
private static GameState openGame() {
|
||||
return new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.empty(), Optional.empty(),
|
||||
@@ -0,0 +1,78 @@
|
||||
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.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RoleArenaPolicyTest {
|
||||
private static final UUID TYRANT = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID PLAYER = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
|
||||
@Test
|
||||
void tyrantVacancyHasPriorityOverVigilanteVacancy() {
|
||||
assertEquals(Optional.of(ArenaRole.TYRANT), RoleArenaPolicy.openRole(game(
|
||||
GameLifecycle.RUNNING, Optional.empty(), Optional.empty(), Optional.empty()
|
||||
)));
|
||||
assertEquals(Optional.of(ArenaRole.VIGILANTE), RoleArenaPolicy.openRole(game(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.empty(), Optional.empty()
|
||||
)));
|
||||
assertEquals(Optional.empty(), RoleArenaPolicy.openRole(game(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.of(PLAYER), Optional.empty()
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pausedGameAndLegacyPendingTyrantDoNotOpenFight() {
|
||||
assertEquals(Optional.empty(), RoleArenaPolicy.openRole(game(
|
||||
GameLifecycle.PAUSED, Optional.empty(), Optional.empty(), Optional.empty()
|
||||
)));
|
||||
assertEquals(Optional.empty(), RoleArenaPolicy.openRole(game(
|
||||
GameLifecycle.RUNNING, Optional.empty(), Optional.empty(),
|
||||
Optional.of(new PendingSelection(PLAYER, Instant.parse("2026-08-24T00:00:00Z")))
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void activeTyrantCannotChallengeAndOptedOutPlayersRemainIneligible() {
|
||||
GameState vigilanteFight = game(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.empty(), Optional.empty()
|
||||
);
|
||||
GameState tyrantFight = game(
|
||||
GameLifecycle.RUNNING, Optional.empty(), Optional.empty(), Optional.empty()
|
||||
);
|
||||
PlayerState eligible = PlayerState.newPlayer(PLAYER, "Player");
|
||||
PlayerState optedOut = new PlayerState(
|
||||
PLAYER, "Player", Optional.empty(), Optional.of(Instant.parse("2026-08-24T12:00:00Z")),
|
||||
TyrantClass.NONE, Optional.empty(), Map.of(), Set.of(), java.util.List.of()
|
||||
);
|
||||
|
||||
assertTrue(RoleArenaPolicy.isEligible(vigilanteFight, ArenaRole.VIGILANTE, eligible));
|
||||
assertTrue(RoleArenaPolicy.isEligible(tyrantFight, ArenaRole.TYRANT, eligible));
|
||||
assertFalse(RoleArenaPolicy.isEligible(vigilanteFight, ArenaRole.VIGILANTE, optedOut));
|
||||
assertFalse(RoleArenaPolicy.isEligible(
|
||||
vigilanteFight, ArenaRole.VIGILANTE, PlayerState.newPlayer(TYRANT, "Tyrant")
|
||||
));
|
||||
}
|
||||
|
||||
private static GameState game(
|
||||
GameLifecycle lifecycle,
|
||||
Optional<UUID> tyrant,
|
||||
Optional<UUID> vigilante,
|
||||
Optional<PendingSelection> pendingTyrant
|
||||
) {
|
||||
return new GameState(
|
||||
lifecycle, tyrant, vigilante, pendingTyrant, Optional.empty(),
|
||||
lifecycle == GameLifecycle.PAUSED
|
||||
? Optional.of(Instant.parse("2026-08-23T12:00:00Z")) : Optional.empty(),
|
||||
Duration.ZERO, 0, 0, Set.of()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.random.RandomGenerator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RoleCandidateSelectorTest {
|
||||
@Test
|
||||
void selectsOnlyRecentlyActiveEligibleNonExcludedPlayer() {
|
||||
Instant now = Instant.parse("2026-08-14T12:00:00Z");
|
||||
UUID tyrant = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
UUID candidate = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
UUID stale = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
Map<UUID, PlayerState> players = Map.of(
|
||||
tyrant, withLogin(PlayerState.newPlayer(tyrant, "Tyrant"), now),
|
||||
candidate, withLogin(PlayerState.newPlayer(candidate, "Candidate"), now.minusSeconds(60)),
|
||||
stale, withLogin(PlayerState.newPlayer(stale, "Stale"), now.minus(Duration.ofHours(25)))
|
||||
);
|
||||
|
||||
Optional<UUID> selected = new RoleCandidateSelector().select(
|
||||
players,
|
||||
Set.of(tyrant),
|
||||
Optional.empty(),
|
||||
now,
|
||||
Duration.ofHours(24),
|
||||
RandomGenerator.of("L64X128MixRandom")
|
||||
);
|
||||
|
||||
assertEquals(Optional.of(candidate), selected);
|
||||
}
|
||||
|
||||
private static PlayerState withLogin(PlayerState player, Instant login) {
|
||||
return new PlayerState(
|
||||
player.playerId(), player.latestName(), Optional.of(login), player.optedOutUntil(),
|
||||
player.tyrantClass(), player.followerOf(), player.cooldownEnds(),
|
||||
player.readyAbilityItems(), player.capturedMobs()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.random.RandomGenerator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RoleMaintenanceServiceTest {
|
||||
@@ -18,19 +17,25 @@ final class RoleMaintenanceServiceTest {
|
||||
private static final UUID OTHER = UUID.fromString("44444444-4444-4444-4444-444444444444");
|
||||
private static final UUID FOLLOWER = UUID.fromString("55555555-5555-5555-5555-555555555555");
|
||||
private static final Instant NOW = Instant.parse("2026-08-14T12:00:00Z");
|
||||
private final RandomGenerator random = RandomGenerator.of("L64X128MixRandom");
|
||||
private final RoleCandidateSelector selector = new RoleCandidateSelector();
|
||||
private final RoleMaintenanceService service = new RoleMaintenanceService(
|
||||
selector,
|
||||
new TyrantSuccessionService(
|
||||
selector, Duration.ofHours(24), Duration.ofHours(24), random
|
||||
),
|
||||
Duration.ofHours(48),
|
||||
Duration.ofHours(24),
|
||||
Duration.ofHours(24),
|
||||
random
|
||||
new TyrantSuccessionService(), Duration.ofHours(48)
|
||||
);
|
||||
|
||||
@Test
|
||||
void legacyPendingTyrantIsClearedIntoArenaVacancy() {
|
||||
GameState game = game(
|
||||
Optional.empty(), Optional.empty(),
|
||||
Optional.of(new PendingSelection(CANDIDATE, NOW.plusSeconds(60))),
|
||||
Optional.empty()
|
||||
);
|
||||
PersistentState state = new PersistentState(game, players(active(CANDIDATE)));
|
||||
|
||||
LifecycleState maintained = service.maintain(state, NOW);
|
||||
|
||||
assertEquals(Optional.empty(), maintained.game().tyrantId());
|
||||
assertEquals(Optional.empty(), maintained.game().pendingTyrant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void legacyPendingVigilanteIsClearedForArenaSuccession() {
|
||||
GameState game = game(
|
||||
@@ -41,7 +46,7 @@ final class RoleMaintenanceServiceTest {
|
||||
active(TYRANT), active(CANDIDATE)
|
||||
));
|
||||
|
||||
LifecycleState maintained = service.maintain(state, Set.of(TYRANT, CANDIDATE), NOW);
|
||||
LifecycleState maintained = service.maintain(state, NOW);
|
||||
|
||||
assertEquals(Optional.empty(), maintained.game().vigilanteId());
|
||||
assertEquals(Optional.empty(), maintained.game().pendingVigilante());
|
||||
@@ -57,7 +62,7 @@ final class RoleMaintenanceServiceTest {
|
||||
active(TYRANT), active(CANDIDATE), active(OTHER)
|
||||
));
|
||||
|
||||
LifecycleState maintained = service.maintain(state, Set.of(TYRANT), NOW);
|
||||
LifecycleState maintained = service.maintain(state, NOW);
|
||||
|
||||
assertEquals(Optional.empty(), maintained.game().vigilanteId());
|
||||
assertEquals(Optional.empty(), maintained.game().pendingVigilante());
|
||||
@@ -78,7 +83,7 @@ final class RoleMaintenanceServiceTest {
|
||||
FOLLOWER, follower, OTHER, active(OTHER)
|
||||
));
|
||||
|
||||
LifecycleState maintained = service.maintain(state, Set.of(TYRANT, OTHER), NOW);
|
||||
LifecycleState maintained = service.maintain(state, NOW);
|
||||
|
||||
assertEquals(Optional.empty(), maintained.game().vigilanteId());
|
||||
assertEquals(Optional.empty(), maintained.players().get(FOLLOWER).followerOf());
|
||||
@@ -86,7 +91,7 @@ final class RoleMaintenanceServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void inactiveTyrantEndsReignAndUsesNormalSuccession() {
|
||||
void inactiveTyrantEndsReignAndOpensArena() {
|
||||
PlayerState staleTyrant = withLogin(
|
||||
active(TYRANT), NOW.minus(Duration.ofHours(48))
|
||||
);
|
||||
@@ -104,8 +109,10 @@ final class RoleMaintenanceServiceTest {
|
||||
TYRANT, staleTyrant, VIGILANTE, active(VIGILANTE), OTHER, successor
|
||||
));
|
||||
|
||||
LifecycleState maintained = service.maintain(state, Set.of(OTHER), NOW);
|
||||
LifecycleState maintained = service.maintain(state, NOW);
|
||||
|
||||
assertEquals(Optional.empty(), maintained.game().tyrantId());
|
||||
assertEquals(Optional.empty(), maintained.game().pendingTyrant());
|
||||
assertEquals(0, maintained.game().tyrantLevel());
|
||||
assertEquals(1, maintained.game().unspentChoices());
|
||||
assertEquals(Set.of(), maintained.game().purchases());
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.random.RandomGenerator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class RoleRelinquishmentServiceTest {
|
||||
@@ -18,14 +17,9 @@ final class RoleRelinquishmentServiceTest {
|
||||
UUID vigilante = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
UUID candidate = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
Instant now = Instant.parse("2026-08-14T12:00:00Z");
|
||||
RoleCandidateSelector selector = new RoleCandidateSelector();
|
||||
RandomGenerator random = RandomGenerator.of("L64X128MixRandom");
|
||||
TyrantSuccessionService succession = new TyrantSuccessionService(
|
||||
selector, Duration.ofHours(24), Duration.ofHours(24), random
|
||||
);
|
||||
TyrantSuccessionService succession = new TyrantSuccessionService();
|
||||
RoleMaintenanceService maintenance = new RoleMaintenanceService(
|
||||
selector, succession, Duration.ofHours(48), Duration.ofHours(24),
|
||||
Duration.ofHours(24), random
|
||||
succession, Duration.ofHours(48)
|
||||
);
|
||||
RoleRelinquishmentService service = new RoleRelinquishmentService(
|
||||
succession, maintenance
|
||||
@@ -53,6 +47,38 @@ final class RoleRelinquishmentServiceTest {
|
||||
assertEquals(Optional.empty(), result.state().game().pendingVigilante());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tyrantRelinquishmentOpensTyrantArenaAndClearsReign() {
|
||||
UUID tyrant = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
UUID vigilante = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
Instant now = Instant.parse("2026-08-14T12:00:00Z");
|
||||
TyrantSuccessionService succession = new TyrantSuccessionService();
|
||||
RoleRelinquishmentService service = new RoleRelinquishmentService(
|
||||
succession, new RoleMaintenanceService(succession, Duration.ofHours(48))
|
||||
);
|
||||
GameState game = new GameState(
|
||||
GameLifecycle.RUNNING, Optional.of(tyrant), Optional.of(vigilante),
|
||||
Optional.empty(), Optional.empty(), Optional.empty(), Duration.ZERO,
|
||||
4, 0, Set.of(TyrantUnlock.STRENGTH)
|
||||
);
|
||||
|
||||
RelinquishmentResult result = service.relinquish(
|
||||
new PersistentState(game, Map.of(
|
||||
tyrant, active(tyrant, now), vigilante, active(vigilante, now)
|
||||
)),
|
||||
tyrant,
|
||||
Set.of(tyrant, vigilante),
|
||||
now
|
||||
);
|
||||
|
||||
assertEquals(RelinquishmentStatus.RELINQUISHED, result.status());
|
||||
assertEquals(Optional.empty(), result.state().game().tyrantId());
|
||||
assertEquals(Optional.empty(), result.state().game().pendingTyrant());
|
||||
assertEquals(Optional.empty(), result.state().game().vigilanteId());
|
||||
assertEquals(0, result.state().game().tyrantLevel());
|
||||
assertEquals(1, result.state().game().unspentChoices());
|
||||
}
|
||||
|
||||
private static PlayerState active(UUID id, Instant now) {
|
||||
PlayerState player = PlayerState.newPlayer(id, id.toString());
|
||||
return new PlayerState(
|
||||
|
||||
@@ -83,7 +83,7 @@ final class TyrantAdminCommandTest {
|
||||
location.worldName().equals("arena_world")
|
||||
&& location.x() == 12.5 && location.y() == 70.0 && location.z() == -4.5
|
||||
));
|
||||
verify(sender).sendMessage(contains("Vigilante arena set"));
|
||||
verify(sender).sendMessage(contains("Shared role arena set"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TyrantArenaSuccessionServiceTest {
|
||||
@Test
|
||||
void eligibleBossWinnerClaimsVacantTyrantRoleWithFreshReign() {
|
||||
UUID winner = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
GameState game = new GameState(
|
||||
GameLifecycle.RUNNING, Optional.empty(), Optional.empty(), Optional.empty(),
|
||||
Optional.empty(), Optional.empty(), Duration.ZERO, 0, 1, Set.of()
|
||||
);
|
||||
PersistentState before = new PersistentState(
|
||||
game, Map.of(winner, PlayerState.newPlayer(winner, "Winner"))
|
||||
);
|
||||
|
||||
LifecycleState claimed = new TyrantArenaSuccessionService().claim(before, winner);
|
||||
|
||||
assertEquals(Optional.of(winner), claimed.game().tyrantId());
|
||||
assertEquals(Optional.empty(), claimed.game().vigilanteId());
|
||||
assertEquals(0, claimed.game().tyrantLevel());
|
||||
assertEquals(1, claimed.game().unspentChoices());
|
||||
assertEquals(Set.of(), claimed.game().purchases());
|
||||
assertEquals(
|
||||
Optional.of(ArenaRole.VIGILANTE),
|
||||
RoleArenaPolicy.openRole(claimed.game())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
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.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -42,7 +38,7 @@ final class TyrantDeathListenerTest {
|
||||
when(manager.game()).thenReturn(game);
|
||||
when(manager.snapshot()).thenReturn(before);
|
||||
TyrantSuccessionService succession = mock(TyrantSuccessionService.class);
|
||||
when(succession.succeed(eq(before), eq(Optional.of(killerId)), any(), any()))
|
||||
when(succession.succeed(eq(before), eq(Optional.of(killerId))))
|
||||
.thenReturn(succeeded);
|
||||
TyrantPresentation presentation = mock(TyrantPresentation.class);
|
||||
Server server = mock(Server.class);
|
||||
@@ -58,8 +54,7 @@ final class TyrantDeathListenerTest {
|
||||
manager,
|
||||
succession,
|
||||
presentation,
|
||||
server,
|
||||
Clock.fixed(Instant.parse("2026-08-14T12:00:00Z"), ZoneOffset.UTC)
|
||||
server
|
||||
);
|
||||
|
||||
listener.onDeath(event);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -9,7 +8,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.random.RandomGenerator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TyrantSuccessionServiceTest {
|
||||
@@ -18,19 +16,14 @@ final class TyrantSuccessionServiceTest {
|
||||
private static final UUID KILLER = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
private static final UUID OTHER = UUID.fromString("44444444-4444-4444-4444-444444444444");
|
||||
private static final Instant NOW = Instant.parse("2026-08-14T12:00:00Z");
|
||||
private final TyrantSuccessionService service = new TyrantSuccessionService(
|
||||
new RoleCandidateSelector(),
|
||||
Duration.ofHours(24),
|
||||
Duration.ofHours(24),
|
||||
RandomGenerator.of("L64X128MixRandom")
|
||||
);
|
||||
private final TyrantSuccessionService service = new TyrantSuccessionService();
|
||||
|
||||
@Test
|
||||
void playerKillerBecomesTyrantAndEntireReignIsReset() {
|
||||
PersistentState before = progressedState();
|
||||
|
||||
LifecycleState after = service.succeed(
|
||||
before, Optional.of(KILLER), Set.of(KILLER, OTHER), NOW
|
||||
before, Optional.of(KILLER)
|
||||
);
|
||||
|
||||
assertEquals(Optional.of(KILLER), after.game().tyrantId());
|
||||
@@ -39,6 +32,9 @@ final class TyrantSuccessionServiceTest {
|
||||
assertEquals(Set.of(), after.game().purchases());
|
||||
assertEquals(Optional.empty(), after.game().vigilanteId());
|
||||
assertEquals(Optional.empty(), after.game().pendingVigilante());
|
||||
assertEquals(
|
||||
Optional.of(ArenaRole.VIGILANTE), RoleArenaPolicy.openRole(after.game())
|
||||
);
|
||||
for (PlayerState player : after.players().values()) {
|
||||
assertEquals(TyrantClass.NONE, player.tyrantClass());
|
||||
assertEquals(Optional.empty(), player.followerOf());
|
||||
@@ -47,15 +43,14 @@ final class TyrantSuccessionServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void environmentalDeathRandomlySelectsRecentPlayerInsteadOfFormerTyrant() {
|
||||
void environmentalDeathOpensTyrantArenaWithoutPendingSelection() {
|
||||
PersistentState before = progressedState();
|
||||
|
||||
LifecycleState after = service.succeed(before, Optional.empty(), Set.of(OTHER), NOW);
|
||||
LifecycleState after = service.succeed(before, Optional.empty());
|
||||
|
||||
UUID selected = after.game().tyrantId().orElseGet(() ->
|
||||
after.game().pendingTyrant().orElseThrow().candidateId()
|
||||
);
|
||||
assertNotEquals(OLD_TYRANT, selected);
|
||||
assertEquals(Optional.empty(), after.game().tyrantId());
|
||||
assertEquals(Optional.empty(), after.game().pendingTyrant());
|
||||
assertEquals(Optional.empty(), after.game().vigilanteId());
|
||||
}
|
||||
|
||||
private static PersistentState progressedState() {
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
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.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class VigilanteArenaPolicyTest {
|
||||
private static final UUID TYRANT = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID PLAYER = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
|
||||
@Test
|
||||
void opensOnlyForRunningGameWithTyrantAndVacantVigilante() {
|
||||
assertTrue(VigilanteArenaPolicy.isOpen(game(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.empty()
|
||||
)));
|
||||
assertFalse(VigilanteArenaPolicy.isOpen(game(
|
||||
GameLifecycle.PAUSED, Optional.of(TYRANT), Optional.empty()
|
||||
)));
|
||||
assertFalse(VigilanteArenaPolicy.isOpen(game(
|
||||
GameLifecycle.RUNNING, Optional.empty(), Optional.empty()
|
||||
)));
|
||||
assertFalse(VigilanteArenaPolicy.isOpen(game(
|
||||
GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.of(PLAYER)
|
||||
)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlyOptedInNonTyrantMayChallenge() {
|
||||
GameState game = game(GameLifecycle.RUNNING, Optional.of(TYRANT), Optional.empty());
|
||||
PlayerState eligible = PlayerState.newPlayer(PLAYER, "Player");
|
||||
PlayerState optedOut = new PlayerState(
|
||||
PLAYER, "Player", Optional.empty(), Optional.of(Instant.parse("2026-08-24T12:00:00Z")),
|
||||
TyrantClass.NONE, Optional.empty(), Map.of(), Set.of(), java.util.List.of()
|
||||
);
|
||||
|
||||
assertTrue(VigilanteArenaPolicy.isEligible(game, eligible));
|
||||
assertFalse(VigilanteArenaPolicy.isEligible(game, optedOut));
|
||||
assertFalse(VigilanteArenaPolicy.isEligible(game,
|
||||
PlayerState.newPlayer(TYRANT, "Tyrant")));
|
||||
}
|
||||
|
||||
private static GameState game(
|
||||
GameLifecycle lifecycle,
|
||||
Optional<UUID> tyrant,
|
||||
Optional<UUID> vigilante
|
||||
) {
|
||||
return new GameState(
|
||||
lifecycle, tyrant, vigilante, Optional.empty(), Optional.empty(),
|
||||
lifecycle == GameLifecycle.PAUSED
|
||||
? Optional.of(Instant.parse("2026-08-23T12:00:00Z")) : Optional.empty(),
|
||||
Duration.ZERO, 0, 0, Set.of()
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user