feat(io): RoomView/Hud views and default Renderings
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
package thb.jeanluc.adventure.io.text;
|
||||||
|
|
||||||
|
/** Status snapshot for the HUD region. */
|
||||||
|
public record Hud(String location, int gold, int turn, boolean lightOn) {}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package thb.jeanluc.adventure.io.text;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Default, layout-free rendering of high-level views into {@link StyledText}. */
|
||||||
|
public final class Renderings {
|
||||||
|
|
||||||
|
private Renderings() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generic room rendering used by non-overriding {@code GameIO}s (e.g. tests). */
|
||||||
|
public static StyledText roomToStyledText(RoomView r) {
|
||||||
|
StyledText.Builder b = StyledText.builder();
|
||||||
|
b.heading(r.name()).plain("\n");
|
||||||
|
b.plain(r.description().stripTrailing());
|
||||||
|
if (!r.items().isEmpty()) {
|
||||||
|
b.plain("\nYou see: ");
|
||||||
|
appendList(b, r.items(), Style.ITEM);
|
||||||
|
b.plain(".");
|
||||||
|
}
|
||||||
|
if (!r.npcs().isEmpty()) {
|
||||||
|
b.plain("\nHere is: ");
|
||||||
|
appendList(b, r.npcs(), Style.NPC);
|
||||||
|
b.plain(".");
|
||||||
|
}
|
||||||
|
if (r.exits().isEmpty()) {
|
||||||
|
b.plain("\nThere are no obvious exits.");
|
||||||
|
} else {
|
||||||
|
b.plain("\nExits: ");
|
||||||
|
appendList(b, r.exits(), Style.EXIT);
|
||||||
|
b.plain(".");
|
||||||
|
}
|
||||||
|
return b.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendList(StyledText.Builder b, List<String> xs, Style st) {
|
||||||
|
for (int i = 0; i < xs.size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
b.plain(", ");
|
||||||
|
}
|
||||||
|
switch (st) {
|
||||||
|
case ITEM -> b.item(xs.get(i));
|
||||||
|
case NPC -> b.npc(xs.get(i));
|
||||||
|
case EXIT -> b.exit(xs.get(i));
|
||||||
|
default -> b.plain(xs.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package thb.jeanluc.adventure.io.text;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Semantic snapshot of a room for rendering. Display-ready strings, no layout. */
|
||||||
|
public record RoomView(String name, String description,
|
||||||
|
List<String> items, List<String> npcs, List<String> exits) {
|
||||||
|
public RoomView {
|
||||||
|
items = List.copyOf(items);
|
||||||
|
npcs = List.copyOf(npcs);
|
||||||
|
exits = List.copyOf(exits);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package thb.jeanluc.adventure.io.text;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import java.util.List;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class RenderingsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void roomToStyledText_includesNameDescriptionItemsNpcsExits() {
|
||||||
|
RoomView room = new RoomView("Kitchen", "A cold kitchen.",
|
||||||
|
List.of("Lamp", "Letter"), List.of("Old Man"), List.of("north", "east"));
|
||||||
|
|
||||||
|
String text = Renderings.roomToStyledText(room).plainText();
|
||||||
|
|
||||||
|
assertThat(text).contains("Kitchen");
|
||||||
|
assertThat(text).contains("A cold kitchen.");
|
||||||
|
assertThat(text).contains("Lamp").contains("Letter");
|
||||||
|
assertThat(text).contains("Old Man");
|
||||||
|
assertThat(text).contains("north").contains("east");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void roomToStyledText_noExits_saysNoObviousExits() {
|
||||||
|
RoomView room = new RoomView("Void", "Nothing.", List.of(), List.of(), List.of());
|
||||||
|
assertThat(Renderings.roomToStyledText(room).plainText())
|
||||||
|
.contains("no obvious exits");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void roomToStyledText_tagsItemSpansWithItemStyle() {
|
||||||
|
RoomView room = new RoomView("Kitchen", "desc",
|
||||||
|
List.of("Lamp"), List.of(), List.of("north"));
|
||||||
|
boolean hasItemSpan = Renderings.roomToStyledText(room).spans().stream()
|
||||||
|
.anyMatch(s -> s.style() == Style.ITEM && s.text().equals("Lamp"));
|
||||||
|
assertThat(hasItemSpan).isTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user