feat: conditional NPC dialogue
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
package thb.jeanluc.adventure.command.impl;
|
package thb.jeanluc.adventure.command.impl;
|
||||||
|
|
||||||
import thb.jeanluc.adventure.command.Command;
|
import thb.jeanluc.adventure.command.Command;
|
||||||
|
import thb.jeanluc.adventure.game.Conditions;
|
||||||
import thb.jeanluc.adventure.game.GameContext;
|
import thb.jeanluc.adventure.game.GameContext;
|
||||||
|
import thb.jeanluc.adventure.model.DialogueLine;
|
||||||
import thb.jeanluc.adventure.model.Npc;
|
import thb.jeanluc.adventure.model.Npc;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -24,7 +26,15 @@ public class TalkCommand implements Command {
|
|||||||
ctx.getIo().write("There is no '" + npcId + "' here to talk to.");
|
ctx.getIo().write("There is no '" + npcId + "' here to talk to.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.getIo().write(npc.get().getName() + " says: " + npc.get().getGreeting());
|
Npc found = npc.get();
|
||||||
|
String line = found.getGreeting();
|
||||||
|
for (DialogueLine dl : found.getDialogue()) {
|
||||||
|
if (Conditions.all(dl.when(), ctx)) {
|
||||||
|
line = dl.text();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.getIo().write(found.getName() + " says: " + line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -113,10 +113,15 @@ public class ReferenceResolver {
|
|||||||
*/
|
*/
|
||||||
public void resolveNpcs(List<NpcDto> npcDtos) {
|
public void resolveNpcs(List<NpcDto> npcDtos) {
|
||||||
for (NpcDto dto : npcDtos) {
|
for (NpcDto dto : npcDtos) {
|
||||||
|
Npc npc = npcs.get(dto.id());
|
||||||
|
if (dto.dialogue() != null) {
|
||||||
|
for (DialogueLineDto dl : dto.dialogue()) {
|
||||||
|
npc.addDialogue(new DialogueLine(ConditionDto.toModelList(dl.when()), dl.text()));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (dto.reactions() == null) {
|
if (dto.reactions() == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Npc npc = npcs.get(dto.id());
|
|
||||||
for (ReactionDto r : dto.reactions()) {
|
for (ReactionDto r : dto.reactions()) {
|
||||||
if (r.onReceive() == null) {
|
if (r.onReceive() == null) {
|
||||||
throw new WorldLoadException(
|
throw new WorldLoadException(
|
||||||
|
|||||||
@@ -10,12 +10,18 @@ import java.util.List;
|
|||||||
* @param description examine description
|
* @param description examine description
|
||||||
* @param greeting text the NPC says on {@code talk}
|
* @param greeting text the NPC says on {@code talk}
|
||||||
* @param reactions list of trigger/response definitions; may be null
|
* @param reactions list of trigger/response definitions; may be null
|
||||||
|
* @param dialogue optional condition-gated dialogue lines; may be null
|
||||||
*/
|
*/
|
||||||
public record NpcDto(
|
public record NpcDto(
|
||||||
String id,
|
String id,
|
||||||
String name,
|
String name,
|
||||||
String description,
|
String description,
|
||||||
String greeting,
|
String greeting,
|
||||||
List<ReactionDto> reactions
|
List<ReactionDto> reactions,
|
||||||
|
List<DialogueLineDto> dialogue
|
||||||
) {
|
) {
|
||||||
|
/** Backward-compatible constructor without dialogue. */
|
||||||
|
public NpcDto(String id, String name, String description, String greeting, List<ReactionDto> reactions) {
|
||||||
|
this(id, name, description, greeting, reactions, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package thb.jeanluc.adventure.model;
|
|||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -34,6 +36,19 @@ public class Npc {
|
|||||||
*/
|
*/
|
||||||
private final Map<String, NpcReaction> reactions;
|
private final Map<String, NpcReaction> reactions;
|
||||||
|
|
||||||
|
/** Optional condition-gated dialogue lines, first match wins; else {@link #greeting}. */
|
||||||
|
@Builder.Default
|
||||||
|
private final List<DialogueLine> dialogue = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a conditional dialogue line.
|
||||||
|
*
|
||||||
|
* @param line the dialogue line
|
||||||
|
*/
|
||||||
|
public void addDialogue(DialogueLine line) {
|
||||||
|
dialogue.add(line);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up a reaction triggered by an item.
|
* Looks up a reaction triggered by an item.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package thb.jeanluc.adventure.command.impl;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import thb.jeanluc.adventure.game.GameContext;
|
||||||
|
import thb.jeanluc.adventure.io.TestIO;
|
||||||
|
import thb.jeanluc.adventure.model.Condition;
|
||||||
|
import thb.jeanluc.adventure.model.DialogueLine;
|
||||||
|
import thb.jeanluc.adventure.model.Npc;
|
||||||
|
import thb.jeanluc.adventure.model.Player;
|
||||||
|
import thb.jeanluc.adventure.model.Room;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class DialogueTest {
|
||||||
|
@Test
|
||||||
|
void dialogueSwitchesWithFlagElseGreeting() {
|
||||||
|
Room room = new Room("k", "K", "d");
|
||||||
|
Npc man = Npc.shell("man", "Old Man", "stooped", "Hello there.");
|
||||||
|
man.addDialogue(new DialogueLine(
|
||||||
|
List.of(new Condition(Condition.Type.FLAG, "power_on")),
|
||||||
|
"The lights are back, bless you!"));
|
||||||
|
room.addNpc(man);
|
||||||
|
Player player = new Player(room, 0);
|
||||||
|
TestIO io = new TestIO();
|
||||||
|
GameContext ctx = new GameContext(null, player, io);
|
||||||
|
|
||||||
|
new TalkCommand().execute(ctx, List.of("man"));
|
||||||
|
assertThat(io.allOutput()).contains("Hello there");
|
||||||
|
|
||||||
|
io.outputs().clear();
|
||||||
|
ctx.getState().set("power_on");
|
||||||
|
new TalkCommand().execute(ctx, List.of("man"));
|
||||||
|
assertThat(io.allOutput()).contains("lights are back").doesNotContain("Hello there");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user