fix(game): null-safe moved() + idempotent begin(); cover multi-step + go_to_room hint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 21:49:47 +02:00
parent af319508f6
commit e27a12e4fe
2 changed files with 29 additions and 2 deletions

View File

@@ -22,6 +22,7 @@ public final class TutorialGuide {
private final CommandRegistry registry; private final CommandRegistry registry;
private int index; private int index;
private boolean active; private boolean active;
private boolean begun;
private Room roomAtStepStart; private Room roomAtStepStart;
public TutorialGuide(Tutorial tutorial, CommandRegistry registry) { public TutorialGuide(Tutorial tutorial, CommandRegistry registry) {
@@ -30,15 +31,17 @@ public final class TutorialGuide {
this.active = !tutorial.isEmpty(); this.active = !tutorial.isEmpty();
} }
/** @return true while the walkthrough is still running. */
public boolean isActive() { public boolean isActive() {
return active; return active;
} }
/** Prints the intro and the first instruction (call once at loop start). */ /** Prints the intro and the first instruction (call once at loop start). */
public void begin(GameContext ctx) { public void begin(GameContext ctx) {
if (!active) { if (!active || begun) {
return; return;
} }
begun = true;
if (tutorial.intro() != null && !tutorial.intro().isBlank()) { if (tutorial.intro() != null && !tutorial.intro().isBlank()) {
ctx.getIo().write(tutorial.intro()); ctx.getIo().write(tutorial.intro());
} }
@@ -108,7 +111,8 @@ public final class TutorialGuide {
} }
private boolean moved(GameContext ctx) { private boolean moved(GameContext ctx) {
return ctx.getPlayer().getCurrentRoom() != roomAtStepStart; return roomAtStepStart != null
&& ctx.getPlayer().getCurrentRoom() != roomAtStepStart;
} }
private boolean sameCommand(String a, String b) { private boolean sameCommand(String a, String b) {

View File

@@ -140,4 +140,27 @@ class TutorialGuideTest {
TutorialGuide g = new TutorialGuide(Tutorial.none(), registry()); TutorialGuide g = new TutorialGuide(Tutorial.none(), registry());
assertThat(g.isActive()).isFalse(); assertThat(g.isActive()).isFalse();
} }
@Test
void advancingPrintsTheNextInstruction() {
GameContext ctx = ctx();
Tutorial t = new Tutorial("I",
List.of(step("do look", "look", 0), step("do take", "take", 1)), "TIPS");
TutorialGuide g = new TutorialGuide(t, registry());
g.begin(ctx);
g.onCommand(ctx, parse("look"));
assertThat(out(ctx)).contains("OK:look").contains("do take");
assertThat(g.isActive()).isTrue();
}
@Test
void goToRoomWithoutMovingGivesHint() {
GameContext ctx = ctx();
Tutorial t = new Tutorial("I", List.of(step("travel", "go_to_room", 0)), "TIPS");
TutorialGuide g = new TutorialGuide(t, registry());
g.begin(ctx);
g.onCommand(ctx, parse("go kitchen")); // non-direction arg but no room change
assertThat(out(ctx)).contains("HINT:go_to_room");
assertThat(g.isActive()).isTrue();
}
} }