fix(status): simplify Tyrant join messaging
Release / release (push) Successful in 2m36s
CI / build (push) Successful in 1m5s

This commit is contained in:
dmg
2026-08-15 09:09:06 -04:00
parent 24c54a7281
commit ea267ff867
4 changed files with 21 additions and 6 deletions
+11
View File
@@ -6,6 +6,17 @@ description: Chronological record of material decisions affecting the Spigot Tyr
# Spigot Tyrant Design Log
## 2026-08-14 — Join messaging refinement completed
- Updated US-011 so join output no longer announces running, paused, or unstarted lifecycle state.
- Eligible unassigned players now see the neutral prompt, `Use /tyrant optout if you do not want to participate in Tyrant events.`
- Role, class, ability, cooldown, and existing opt-out details remain unchanged.
- Verified both assigned and unassigned join output with automated tests.
## 2026-08-14 — Join messaging refinement started
- US-011 is reopened to remove lifecycle announcements from join output and make the opt-out prompt refer generally to Tyrant events.
## 2026-08-14 — Opt-out combat protection completed
- Extended US-011 with bidirectional combat isolation between opted-out players and the Tyrant, Assassin, Fixer, or Tamer while the event is running.
@@ -11,7 +11,8 @@ As a **player**, I want to understand my status and control my eligibility so th
## Acceptance criteria
- [x] On join, a player is told the game status and their role, class, subclass, abilities, and relevant cooldowns.
- [x] On join, lifecycle state is omitted while role, class, subclass, ability, and relevant cooldown information remains available when applicable.
- [x] Eligible players are invited to opt out of Tyrant events without implying that an event is currently running.
- [x] An unassigned opted-in player is told that they are eligible and shown the opt-out command.
- [x] An opted-out player is told when they may opt back in.
- [x] An unassigned player can opt out through a command.
@@ -15,7 +15,6 @@ public final class PlayerStatusMessages {
Instant now
) {
List<String> messages = new ArrayList<>();
messages.add("Game: " + game.lifecycle());
boolean hasRole = false;
if (game.tyrantId().filter(player.playerId()::equals).isPresent()) {
messages.add("Role: TYRANT. Level " + game.tyrantLevel() + ", choices "
@@ -45,8 +44,8 @@ public final class PlayerStatusMessages {
messages.add("Participation: OPTED OUT. You may use /tyrant optin at "
+ player.optedOutUntil().orElseThrow() + ".");
} else if (!hasRole) {
messages.add("You currently have no role or class. Use /tyrant optout "
+ "if you do not want to participate.");
messages.add("Use /tyrant optout if you do not want to participate "
+ "in Tyrant events.");
}
return List.copyOf(messages);
}
@@ -1,5 +1,6 @@
package games.dmg.spigottyrant;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Duration;
@@ -29,7 +30,8 @@ final class PlayerStatusMessagesTest {
List<String> messages = PlayerStatusMessages.forJoin(game, player, NOW);
assertTrue(messages.stream().anyMatch(message -> message.contains("Game: RUNNING")));
assertFalse(messages.stream().anyMatch(message -> message.startsWith("Game:")));
assertFalse(messages.stream().anyMatch(message -> message.startsWith("Tyrant event:")));
assertTrue(messages.stream().anyMatch(message -> message.contains("Role: VIGILANTE")));
assertTrue(messages.stream().anyMatch(message -> message.contains("Class: ASSASSIN")));
assertTrue(messages.stream().anyMatch(message -> message.contains("ASSASSIN_INVISIBILITY")
@@ -42,6 +44,8 @@ final class PlayerStatusMessagesTest {
GameState.empty(), PlayerState.newPlayer(PLAYER, "Player"), NOW
);
assertTrue(messages.stream().anyMatch(message -> message.contains("/tyrant optout")));
assertTrue(messages.contains(
"Use /tyrant optout if you do not want to participate in Tyrant events."
));
}
}